Ich verstehe, dass dies <? super T>
jede Superklasse von T
(Elternklasse T
jeder Stufe) darstellt. Aber ich habe wirklich Mühe, mir ein Beispiel aus dem wirklichen Leben für diese generisch gebundene Wildcard vorzustellen.
Ich verstehe was <? super T>
bedeutet und ich habe diese Methode gesehen:
public class Collections {
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
for (int i = 0; i < src.size(); i++)
dest.set(i, src.get(i));
}
}
Ich suche nach einem Beispiel für einen realen Anwendungsfall, in dem diese Konstruktion verwendet werden kann, und nicht nach einer Erklärung dessen, was sie ist.
java
generics
inheritance
super
BanzaiTokyo
quelle
quelle
? super T
könnte hilfreich sein.Antworten:
Das einfachste Beispiel, an das ich denken kann, ist:
public static <T extends Comparable<? super T>> void sort(List<T> list) { list.sort(null); }
aus dem gleichen genommen
Collections
. Auf diese WeiseDog
kann a implementierenComparable<Animal>
und wenn diesAnimal
bereits implementiert ist,Dog
muss nichts getan werden.EDIT für ein echtes Beispiel:
Nach ein paar E-Mail-Ping-Pongs darf ich ein echtes Beispiel von meinem Arbeitsplatz aus präsentieren (yay!).
Wir haben eine Schnittstelle namens
Sink
(es spielt keine Rolle, was sie tut), die Idee ist, dass sich Dinge ansammeln . Die Erklärung ist ziemlich trivial (vereinfacht):interface Sink<T> { void accumulate(T t); }
Offensichtlich gibt es eine Hilfsmethode, die a nimmt
List
und seine Elemente in a entleertSink
(es ist etwas komplizierter, aber um es einfach zu machen):public static <T> void drainToSink(List<T> collection, Sink<T> sink) { collection.forEach(sink::accumulate); }
Das ist einfach richtig? Gut...
Ich kann ein haben
List<String>
, aber ich möchte es auf ein ablassenSink<Object>
- das ist eine ziemlich häufige Sache, die wir für uns tun müssen; aber das wird scheitern:Sink<Object> sink = null; List<String> strings = List.of("abc"); drainToSink(strings, sink);
Damit dies funktioniert, müssen wir die Erklärung ändern in:
public static <T> void drainToSink(List<T> collection, Sink<? super T> sink) { .... }
quelle
Source
als Beispiel ... das ist im Grunde das Doppelte IhresSink
Beispiels.list
, es wird nur ein Produzent; Wie gesagt, dies ist ein vereinfachtes Beispiel ...Angenommen, Sie haben diese Klassenhierarchie: Katze erbt von Mammal, was wiederum von Animal erbt.
List<Animal> animals = new ArrayList<>(); List<Mammal> mammals = new ArrayList<>(); List<Cat> cats = ...
Diese Aufrufe sind gültig:
Collections.copy(animals, mammals); // all mammals are animals Collections.copy(mammals, cats); // all cats are mammals Collections.copy(animals, cats); // all cats are animals Collections.copy(cats, cats); // all cats are cats
Diese Aufrufe sind jedoch nicht gültig:
Collections.copy(mammals, animals); // not all animals are mammals Collections.copy(cats, mammals); // not all mammals are cats Collections.copy(cats, animals); // mot all animals are cats
Die Methodensignatur stellt also einfach sicher, dass Sie von einer spezifischeren Klasse (in der Vererbungshierarchie niedriger) in eine allgemeinere Klasse (in der Vererbungshierarchie höher) kopieren und nicht umgekehrt.
quelle
super
Schlüsselwort ist jedoch nicht erforderlich, damit dies funktioniert. Diese Unterschrift hätte auch identisches Verhalten aufgedeckt:public static <T> void copy(List<T> dest, List<? extends T> src) {
Collections.<Mammal>copy(animals, cats);
- aber ich weiß nicht, warum jemand Code wie diesen schreiben würde / sollte ...Schauen Sie sich zum Beispiel die
Collections.addAll
Methodenimplementierung an:public static <T> boolean addAll(Collection<? super T> c, T... elements) { boolean result = false; for (T element : elements) result |= c.add(element); return result; }
Hier können die Elemente in jede Sammlung eingefügt werden, deren Elementtyp ein Supertyp des
T
Elementtyps ist.Ohne Platzhalter mit niedrigerer Grenze:
public static <T> boolean addAll(Collection<T> c, T... elements) { ... }
Folgendes wäre ungültig gewesen:
List<Number> nums = new ArrayList<>(); Collections.<Integer>addAll(nums , 1, 2, 3);
weil der Begriff
Collection<T>
restriktiver ist alsCollection<? super T>
.Ein anderes Beispiel:
Predicate<T>
Schnittstelle in Java, die einen<? super T>
Platzhalter in den folgenden Methoden verwendet:default Predicate<T> and(Predicate<? super T> other); default Predicate<T> or(Predicate<? super T> other);
<? super T>
ermöglicht es, einen größeren Bereich verschiedener Prädikate zu verketten, zum Beispiel:Predicate<String> p1 = s -> s.equals("P"); Predicate<Object> p2 = o -> o.equals("P"); p1.and(p2).test("P"); // which wouldn't be possible with a Predicate<T> as a parameter
quelle
Collections.addAll(nums, 1, 2, 3)
mit beiden Methodensignaturen schreiben .Angenommen, Sie haben eine Methode:
passToConsumer(Consumer<? super SubType> consumer)
dann rufen Sie diese Methode mit jeder auf,
Consumer
die verbrauchen kannSubType
:Zum Beispiel:
class Animal{} class Dog extends Animal{ void putInto(List<? super Dog> list) { list.add(this); } }
So kann ich das
Dog
inList<Animal>
oder setzenList<Dog>
:List<Animal> animals = new ArrayList<>(); List<Dog> dogs = new ArrayList<>(); Dog dog = new Dog(); dog.putInto(dogs); // OK dog.putInto(animals); // OK
Wenn Sie die
putInto(List<? super Dog> list)
Methode ändern inputInto(List<Animal> list)
:Dog dog = new Dog(); List<Dog> dogs = new ArrayList<>(); dog.putInto(dogs); // compile error, List<Dog> is not sub type of List<Animal>
oder
putInto(List<Dog> list)
:Dog dog = new Dog(); List<Animal> animals = new ArrayList<>(); dog.putInto(animals); // compile error, List<Animal> is not sub type of List<Dog>
quelle
Consumer
oderconsume
dies automatisch in diePECS
Kategorie fällt , ohne zu sagen, dass dies schlecht ist, gute BeispieleIch habe ein Webradio geschrieben, also hatte ich die Klasse
MetaInformationObject
, die die Superklasse für PLS- und M3U-Wiedergabelisten war. Ich hatte einen Auswahldialog, also hatte ich:public class SelectMultipleStreamDialog <T extends MetaInformationObject> public class M3UInfo extends MetaInformationObject public class PLSInfo extends MetaInformationObject
Diese Klasse hatte eine Methode
public T getSelectedStream()
.Der Anrufer erhielt also ein T vom konkreten Typ (PLS oder M3U), musste aber an der Oberklasse arbeiten, sodass es eine Liste gab :
List<T super MetaInformationObject>
. wo das Ergebnis hinzugefügt wurde.So könnte ein generischer Dialog mit den konkreten Implementierungen umgehen und der Rest des Codes könnte in der Oberklasse funktionieren.
Hoffe das macht es etwas klarer.
quelle
Betrachten Sie dieses einfache Beispiel:
List<Number> nums = Arrays.asList(3, 1.2, 4L); Comparator<Object> numbersByDouble = Comparator.comparing(Object::toString); nums.sort(numbersByDouble);
Hoffentlich ist dies ein etwas überzeugender Fall: Sie könnten sich vorstellen, die Zahlen zu Anzeigezwecken sortieren zu wollen (für die der toString eine vernünftige Reihenfolge darstellt), aber
Number
selbst nicht vergleichbar.Dies wird kompiliert, weil
integers::sort
aComparator<? super E>
. Wenn es nur einComparator<E>
(woE
in diesem Fall istNumber
) dauert, kann der Code nicht kompiliert werden, daComparator<Object>
es sich nicht um einen Subtyp von handeltComparator<Number>
(aus Gründen, die Ihre Frage angibt, dass Sie bereits verstanden haben, werde ich nicht darauf eingehen).quelle
Hier dienen Sammlungen als gutes Beispiel.
Wie in 1 angegeben ,
List<? super T>
können SieList
Elemente vom Typ erstellen , die weniger abgeleitet sind alsT
, sodass Elemente enthalten werden, die vomT
Typ erben, vom TypT
undT
vom erben.Auf der anderen Seite
List<? extends T>
können Sie ein definierenList
, das nur Elemente enthalten kann, von denen geerbt wirdT
(in einigen Fällen nicht einmal vom TypT
).Dies ist ein gutes Beispiel:
public class Collections { public static <T> void copy(List<? super T> dest, List<? extends T> src) { for (int i = 0; i < src.size(); i++) dest.set(i, src.get(i)); } }
Hier möchten Sie einen
List
weniger abgeleiteten Typ auf einenList
weniger abgeleiteten Typ projizieren . HierList<? super T>
versichert uns, dass alle Elemente vonsrc
in der neuen Kollektion gültig sind.1 : Unterschied zwischen <? Super T> und <? erweitert T> in Java
quelle
Angenommen, Sie haben:
class T {} class Decoder<T> class Encoder<T> byte[] encode(T object, Encoder<? super T> encoder); // encode objects of type T T decode(byte[] stream, Decoder<? extends T> decoder); // decode a byte stream into a type T
Und dann:
class U extends T {} Decoder<U> decoderOfU; decode(stream, decoderOfU); // you need something that can decode into T, I give you a decoder of U, you'll get U instances back Encoder<Object> encoderOfObject; encode(stream, encoderOfObject);// you need something that can encode T, I give you something that can encode all the way to java.lang.Object
quelle
Hierfür fallen einige Beispiele aus dem wirklichen Leben ein. Das erste, das ich ansprechen möchte, ist die Idee, dass ein reales Objekt für "improvisierte" Funktionen verwendet wird. Stellen Sie sich vor, Sie haben einen Steckschlüssel:
public class SocketWrench <T extends Wrench>
Der offensichtliche Zweck eines Steckschlüssels ist es also, als
Wrench
. Wenn Sie jedoch bedenken, dass ein Schraubenschlüssel zur Not verwendet werden kann, um einen Nagel einzuschlagen, können Sie eine Vererbungshierarchie haben, die folgendermaßen aussieht:public class SocketWrench <T extends Wrench> public class Wrench extends Hammer
In diesem Szenario können Sie anrufen
socketWrench.pound(Nail nail = new FinishingNail())
, obwohl dies als atypische Verwendung für a angesehen wirdSocketWrench
.Während der gesamten Zeit
SocketWrench
hätte der Zugriff darauf, Methoden aufrufen zu können, alsapplyTorque(100).withRotation("clockwise").withSocketSize(14)
ob sie alsSocketWrench
statt nur alsWrench
, statt als verwendet würdenHammer
.quelle