Ich habe einige Fragen zu generischen Platzhaltern in Java:
Was ist der Unterschied zwischen
List<? extends T>
undList<? super T>
?Was ist ein begrenzter Platzhalter und was ist ein unbegrenzter Platzhalter?
Ich habe einige Fragen zu generischen Platzhaltern in Java:
Was ist der Unterschied zwischen List<? extends T>
und List<? super T>
?
Was ist ein begrenzter Platzhalter und was ist ein unbegrenzter Platzhalter?
Antworten:
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 .
<? super C>
würde bedeuten, dass Ihr Typ auf etwas oben C
in der Typhierarchie beschränkt ist . (Entschuldigung für die extrem späte Antwort. Ich denke, wir hatten vor 2 Jahren keine Kommentarbenachrichtigungen?)
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 B
dem 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.
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?
Josh Bloch hat auch eine gute Erklärung, wann er sie verwenden soll, super
und extends
in diesem Google Io-Videogespräch erwähnt er den Produzenten- extends
Konsumentensuper
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
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).
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));
}
}
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:
List<A>
, sollten wir diese Methode angeben dürfen List<A-sub>
. (Weil A-Sub ein A ist)List<A>
, sollten wir diese Methode angeben dürfen List<A-super>
. (Weil A ein A-Super ist)