Generische Java-Typen können keine List
Verschiedene Typen
//Wenn die Parameter vom Typ OK identisch sind
List<Integer> foo = new ArrayList<Integer>();
// NG List<Integer>Ist eine Liste<Number>Kann nicht konvertiert werden
List<Number> bar = foo;
//Ebenso ist eine Konvertierung auch bei NG-Initialisierung nicht möglich
List<Number> foo = new ArrayList<Integer>();
Daher können durch Verwendung des ** Boundary Wildcard-Typs ** ** Typparameter mit einer gewissen Breite ** deklariert werden. Es gibt jedoch zwei Arten von Grenz-Platzhaltertypen. Dies liegt daran, dass beim Festlegen der Typgrenzen in jeder Situation das Problem auftritt, dass das Verarbeitungssystem den Typ nicht festlegen kann. Schauen wir uns das Problem einzeln an.
Die folgende Fruchtklasse wird zur Erklärung verwendet.
Generic<? extends T>
Wenn Sie einen Typparameter wie oben angeben, ist der Typparameter ** Platzhaltertyp **. Der Typ ? Erweitert T </ kbd> bedeutet ** T oder einen Typ, der alle seine Unterklassen ** darstellt.
Wenn Sie den Typ "List <? Extends Fruit>" verwenden, können Sie aus der ** Fruit-Klasse oder dem List-Typ ** konvertieren, der die Unterklassen angibt.
Liste der konvertierbaren Liste
List<? extends Fruit> basket = new ArrayList<Fruit>(); // 1
List<? extends Fruit> basket = new ArrayList<Melon>(); // 2
List<? extends Fruit> basket = new ArrayList<Lemon>(); // 3
Als nächstes sollten Sie Elemente aus der Variablen "List <? Extends Fruit>" abrufen. Bitte beachten Sie, dass die Korbvariable als Fruchtklasse abgerufen werden kann, unabhängig davon, ob es sich um 1, 2 oder 3 handelt.
Ziehen Sie in Betracht, Elemente aus der Variablen "List <? Extends Fruit>" abzurufen. Bei einem Platzhaltertyp mit einer Obergrenze handelt es sich ** zumindest um einen Typ, der die T-Klasse und ihre Unterklassen darstellt **, sodass beim Abrufen eines Elements aus der Liste die Obergrenze T angegeben werden kann.
Konvertieren Sie den begrenzten Platzhaltertyp in den T-Typ
List<? extends Fruit> fruits = ...
//kein Problem
Fruit fruit = fruits.get(i);
Dies ist eine Konvertierung vom Typ "? Erweitert T" zum Typ "T".
Wenn Sie ein Element aus der Warenkorbvariablen abrufen, prüfen Sie, ob seine Unterklasse angegeben werden kann. Der Typ "List <? Extends Fruit>" kann aus mindestens den folgenden drei Listentypen konvertiert werden.
ArrayList<Fruit>
ArrayList<Melon>
ArrayList<Lemon>
Daher ist es beim Abrufen eines Elements nicht möglich, eine Unterklasse anzugeben, die aus mehreren Serien stammt.
Der begrenzte Platzhaltertyp kann nicht in einen Unterklassentyp konvertiert werden
List<? extends Fruit> basket = ...
//NG-Kompilierungsfehler Kann nicht in Melonentyp konvertiert werden
Melon fruit = basket.get(i);
Wenn der Typ "? Extends Fruit" in eine Unterklasse konvertiert werden kann, treten die folgenden Inkonsistenzen auf.
Konvertieren Sie den gekappten Platzhaltertyp in den T-Unterklassentyp 1
//Machen Sie eine Melonenliste
List<Melon> melonList = new ArrayList<Melon>()
melonList.add(new Melon());
// List<Melon>Listentyp melonList<? extends Fruit>In Typ konvertieren
List<? extends Fruit> basket = melonList;
//Wenn es in eine Unterklasse konvertiert werden kann, kann es als Zitrone empfangen werden.
Lemon fruit = basket.get(i);
Daher ist eine Konvertierung in Unterklassen nicht zulässig. Daher ist eine Konvertierung vom Typ "Erweitert T" in den Typ "T" der Unterklasse "T" nicht möglich.
Als nächstes sollten Sie ein Element zur Variablen "List <? Extends Fruit> Früchte" hinzufügen. Da wir vom Typ "? Extends Fruit`" in den Typ "Fruit Type" konvertieren können, können wir der folgenden Fruchtvariablen anscheinend ein Fruit-Objekt hinzufügen.
Schreiben Sie in einen begrenzten Platzhaltertyp
List<? extends Fruit> fruits = new ArrayList<Fruit>();
//Scheint auf den ersten Blick möglich
fruits.add(new Fruit());
Wenn dies jedoch zulässig ist, treten die folgenden Inkonsistenzen auf.
Das Schreiben in einen begrenzten Platzhaltertyp ist inkonsistent
//Die Realität der Früchte ist eine Melonenliste
List<? extends Fruit> fruits = new ArrayList<Melon>();
//Die Zitronenklasse ist eine Unterklasse der Obstklasse
Fruit fruit = new Lemon();
//Zitrone kann zur Melonenliste hinzugefügt werden
fruits.add(fruit);
Daher ist es nicht möglich, vom Betontyp "T" oder "T-Unterklasse" in den Typ "Erweitert T" zu konvertieren. Dies bedeutet, dass Sie keine Elemente hinzufügen können, wenn Sie einen Listentyp mit einem begrenzten Platzhalter deklarieren. Hier kommt ein anderer Rand-Platzhaltertyp ins Spiel.
Generic<? super T>
Wenn Sie einen Typparameter wie oben angeben, handelt es sich um einen Platzhaltertyp mit einer Untergrenze. Der Typ ? Super T </ kbd> bedeutet ** T oder einen Typ, der alle seine Superklassen ** darstellt.
Sie können von ** Fruchtklasse oder Listentyp ** in den Typ ** Liste \ <? Super Melon > ** konvertieren, dessen Typparameter die Fruchtklasse oder ihre Superklasse ist.
Liste der konvertierbaren Liste
List<? super Melon> baskets = new ArrayList<Melon>(); // 1
List<? super Melon> baskets = new ArrayList<Fruit>(); // 2
List<? super Melon> baskets = new ArrayList<Food>(); // 3
List<? super Melon> baskets = new ArrayList<Object>(); // 4
Dieses Mal werde ich zuerst darüber nachdenken, der Warenkorbvariablen Elemente hinzuzufügen. Bitte beachten Sie, dass die Melonenklasse zur Warenkorbvariablen hinzugefügt werden kann, wenn 1 bis 4 1, 2, 3 und 4 sind.
Um ein Element zu einer "List <? Super Melon> -Korb" -Variablen hinzuzufügen, ist es im Fall eines Platzhaltertyps mit einer Untergrenze ** ein Typ, der mindestens eine T-Klasse oder eine höhere Superklasse darstellt **, also ein Element zu einem Listentyp Beim Hinzufügen sollten Sie in der Lage sein, die Untergrenze T anzugeben.
Überprüfen Sie anhand des Beispiels, ob Sie eine nach der anderen hinzufügen können.
Wenn List <? Super Melon> Früchte = new ArrayList <Object> ()
Zur Objektliste hinzufügen
// OK
fruits.add(new Melon())
// OK
fruits.add(new WaterMelon());
Wenn List <? Super Melon> Früchte = neue ArrayList <Food> ()
Zur Lebensmittelliste hinzufügen
// OK
fruits.add(new Melon())
// OK
fruits.add(new WaterMelon());
Wenn List <? Super Melon> Früchte = neue ArrayList <Melon> ()
Zur Melonenliste hinzufügen
// OK
fruits.add(new Melon());
// OK
fruits.add(new WaterMelon());
Mit anderen Worten, es ist möglich, von einem Typ "T" oder "T" in einen Typ "Super T" zu konvertieren.
Der Typ ? Super Melon </ kbd> ist ein "Typ, der die Melonenklasse und alle ihre Superklassen darstellt". Außerdem erben alle Java-Klassen vom Typ ** Object **. Daher ist eine Konvertierung vom Typ "? Super T" in den Typ "Objekt" möglich.
Konvertierung vom Platzhaltertyp mit Untergrenze zum Objekttyp
Object object = basket.get(i)
Grenzparametertypen können verwendet werden, um eine Vielzahl von generischen Typen darzustellen.
Umrechnungstabelle für Platzhalter und konkrete Typen mit Obergrenze
Ja Nein | Konvertierungsquelle | Conversion-Ziel | Es bedeutet |
---|---|---|---|
OK | ? extends T |
T |
T Kann Typwert erhalten |
NG | T |
? extends T |
Kann nicht schreiben |
Umrechnungstabelle für Platzhalter und konkrete Typen mit Untergrenze
Ja Nein | Konvertierungsquelle | Conversion-Ziel | Es bedeutet |
---|---|---|---|
OK | ? super T |
Object |
Wert kann als Objekttyp erhalten werden |
OK | T |
? super T |
T-Typ-Wert kann geschrieben werden |
OK | T Unterklasse |
? super T |
Beschreibbarer Wert des Unterklassentyps von T. |
Im Allgemeinen gibt es einen Zauber namens ** PECS **, und der begrenzte Platzhaltertyp mit Obergrenze wird manchmal als Produzent bezeichnet, und der begrenzte Platzhaltertyp mit Untergrenze wird manchmal als Verbraucher bezeichnet.
Produzent-Spezialisiert auf Wertschöpfung
Verbraucherspezialisiert auf den Erhalt von Werten
Appendix
Möglicherweise haben Sie gehört, dass die Liste nicht geschrieben werden kann, wenn Sie einen begrenzten Platzhaltertyp mit einer oberen Grenze angeben.
Dies bedeutet einfach, dass Sie nicht:
** Kann nicht von "konkreter Typ" in "gekappten Platzhaltertyp" konvertiert werden **
Wenn Sie ein generisches Typobjekt mit einem Platzhalter für die obere Grenze deklarieren, erfolgt die obige Konvertierung, wenn Sie eine Funktion mit diesem Typparameter aufrufen.
Betrachten Sie die unten stehende Stack \ <T > -Klasse.
Stapelklasse
class Stack<T> {
void push(T e) {}
}
Deklarieren Sie die Stack \ <T > -Klasse mit einem begrenzten Platzhaltertyp.
Stack<? extends Fruit> basket = new Stack<Fruit>();
Zu diesem Zeitpunkt ist der Typparameter T der Korbvariablen vom Typ "Extends Fruit". Die Push-Methode entspricht also:
Wenn der Stack-Typ-Parameter ein begrenzter Platzhalter ist
void push(`? extends Fruit` e) {}
Daher führt das Aufrufen dieser Push-Methode zu einer Konvertierung von einem "Obst" in einen "Extended Fruit" -Typ. Und diese Umwandlung ist NG.
//NG Fruchtart`? extends Fruit`Kann nicht in Typ konvertiert werden
basket.push(new Fruit());
Recommended Posts