Java Generics (Platzhalter)

109

Ich habe einige Fragen zu generischen Platzhaltern in Java:

  1. Was ist der Unterschied zwischen List<? extends T>und List<? super T>?

  2. Was ist ein begrenzter Platzhalter und was ist ein unbegrenzter Platzhalter?

Pablo Fernandez
quelle
Für den Fall, dass die Antwort von Ted Gao gelöscht wird (da sie nur mit Links versehen war), finden Sie hier den Blog-Beitrag, auf den sie verlinkt hat.
Royhowie

Antworten:

123

In Ihrer ersten Frage <? extends T>und <? super T>sind Beispiele für begrenzte Platzhalter. Ein unbegrenzter Platzhalter sieht aus <?>und bedeutet im Grunde <? extends Object>. Es bedeutet locker, dass das Generikum jeder Typ sein kann. Ein beschränktes Platzhalter ( <? extends T>oder <? super T>) legt eine Beschränkung des Typs von selbst , dass es entweder hat zu verlängern einen bestimmten Typ ( <? extends T>als eine obere Grenze bezeichnet), oder nur ein Vorfahre eines spezifischen Typs sein ( <? super T>als untere Grenze bezeichnet) .

Die Java-Tutorials enthalten einige ziemlich gute Erklärungen zu Generika in den Artikeln Wildcards und More Fun with Wildcards .

Bill die Eidechse
quelle
Nur um es richtig zu machen, wenn A <B und B <C dann: <A erweitert C> ist falsch?
Pablo Fernandez
Wenn mit A <B gemeint ist, dass A B erweitert, dann erweitert A C. Sie würden dies jedoch nicht in der Wildcard-Syntax verwenden, würden Sie sagen <? erweitert C>, um Ihre Auswahl auf A oder B zu beschränken.
Bill the Lizard
und in diesem Fall, wenn ich <sage? super C> was wäre der Unterschied?
Pablo Fernandez
3
Ich
Zach Scrivena
3
@Pablo: <? super C>würde bedeuten, dass Ihr Typ auf etwas oben Cin der Typhierarchie beschränkt ist . (Entschuldigung für die extrem späte Antwort. Ich denke, wir hatten vor 2 Jahren keine Kommentarbenachrichtigungen?)
Bill the Lizard
49

Wenn Sie eine Klassenhierarchie A haben, ist B eine Unterklasse von A, und C und D sind beide Unterklasse von B wie unten

class A {}
class B extends A {}
class C extends B {}
class D extends B {}

Dann

List<? extends A> la;
la = new ArrayList<B>();
la = new ArrayList<C>();
la = new ArrayList<D>();

List<? super B> lb;
lb = new ArrayList<A>(); //fine
lb = new ArrayList<C>(); //will not compile

public void someMethod(List<? extends B> lb) {
    B b = lb.get(0); // is fine
    lb.add(new C()); //will not compile as we do not know the type of the list, only that it is bounded above by B
}

public void otherMethod(List<? super B> lb) {
    B b = lb.get(0); // will not compile as we do not know whether the list is of type B, it may be a List<A> and only contain instances of A
    lb.add(new B()); // is fine, as we know that it will be a super type of A 
}

Ein begrenzter Platzhalter entspricht ? extends Bdem Typ B. Das heißt, der Typ ist unbekannt, aber es kann eine "Bindung" darauf platziert werden. In diesem Fall ist es durch eine Klasse begrenzt, die eine Unterklasse von B ist.

oxbow_lakes
quelle
Ich nehme an, List<? super B>beschreibt als Liste akzeptiert den Typ, der übergeordnete Klasse der Klasse B ist ? Aus diesem Grund kompilieren C und D hmm nicht?
Volkan Güven
38

Josh Bloch hat auch eine gute Erklärung, wann er sie verwenden soll, superund extendsin diesem Google Io-Videogespräch erwähnt er den Produzenten- extendsKonsumentensuper Mnemonik.

Aus den Präsentationsfolien:

Angenommen, Sie möchten Bulk-Methoden hinzufügen Stack<E>

void pushAll(Collection<? extends E> src);

- src ist ein E-Produzent

void popAll(Collection<? super E> dst);

- dst ist ein E-Verbraucher

leer
quelle
Ich habe Blochs Buch gelesen, aber ich kann immer noch nicht den Unterschied zwischen Extend und Super in diesem speziellen Fall erkennen.
Pablo Fernandez
Sehen Sie sich das Video an, ich denke es ist ziemlich klar. Ich denke auch, dass Sie eine andere Frage zu diesem Thema stellen sollten: "Was ist der Unterschied zwischen List <? Erweitert T> und List <? Super T>", wo Sie hoffentlich mehr Antworten erhalten. (Wenn Sie dies tun, fügen Sie einen Link von hier hinzu)
leer
2
Betten - warum nicht das Beispiel in diese Antwort aufnehmen, um es vollständig und eigenständig zu machen? Links sind kurzlebig.
James Schek
3

Es kann vorkommen, dass Sie die Arten von Typen einschränken möchten, die an einen Typparameter übergeben werden dürfen. Beispielsweise möchte eine Methode, die mit Zahlen arbeitet, möglicherweise nur Instanzen von Number oder deren Unterklassen akzeptieren. Dafür sind begrenzte Typparameter gedacht.

Collection<? extends MyObject> 

bedeutet, dass es alle Objekte akzeptieren kann, die eine IS-A- Beziehung zu MyObject haben (dh jedes Objekt, das ein Typ von myObject ist, oder wir können jedes Objekt einer beliebigen Unterklasse von MyObject sagen) oder ein Objekt der MyObject-Klasse.

Beispielsweise:

class MyObject {}

class YourObject extends MyObject{}

class OurObject extends MyObject{}

Dann,

Collection<? extends MyObject> myObject; 

akzeptiert nur MyObject oder untergeordnete Elemente von MyObject (dh Objekte vom Typ OurObject oder YourObject oder MyObject, jedoch keine Objekte der Oberklasse von MyObject).

Sandeep Kumar
quelle
1

Allgemein,

Wenn eine Struktur Elemente mit einem Formulartyp enthält ? extends E, können wir Elemente aus der Struktur entfernen, aber keine Elemente in die Struktur einfügen

List<Integer> ints = new ArrayList<Integer>();
ints.add(1);
ints.add(2);
List<? extends Number> nums = ints;
nums.add(3.14); // compile-time error
assert ints.toString().equals("[1, 2, 3.14]"); 

Um Elemente in die Struktur einzufügen, benötigen wir eine andere Art von Platzhalter namens Wildcards with super:

 List<Object> objs = Arrays.<Object>asList(2, 3.14, "four");
    List<Integer> ints = Arrays.asList(5, 6);
    Collections.copy(objs, ints);
    assert objs.toString().equals("[5, 6, four]");

    public static <T> void copy(List<? super T> dst, List<? extends T> src) {
          for (int i = 0; i < src.size(); i++) {
                dst.set(i, src.get(i));
         }
    }
Prateek Joshi
quelle
1

Generische Platzhalter werden erstellt, um Methoden, die mit Collection arbeiten, wiederverwendbarer zu machen.

Wenn eine Methode beispielsweise einen Parameter hat List<A>, können wir nur List<A>diese Methode angeben. Unter bestimmten Umständen ist es eine Verschwendung für die Funktion dieser Methode:

  1. Wenn diese Methode nur Objekte liest List<A>, sollten wir diese Methode angeben dürfen List<A-sub>. (Weil A-Sub ein A ist)
  2. Wenn diese Methode nur Objekte einfügt List<A>, sollten wir diese Methode angeben dürfen List<A-super>. (Weil A ein A-Super ist)
Taoxiaopang
quelle