Unterschied zwischen und in Java

Was ist der Unterschied zwischen List List und List List ?

Ich habe früher List List , aber es erlaubt mir nicht, Elemente hinzuzufügen, um es list.add(e) , während die List List tut.

extends

Die Platzhalterdeklaration von List< ? extends Number> foo3 List< ? extends Number> foo3 bedeutet, dass es sich um legale Zuweisungen handelt:

 List< ? extends Number> foo3 = new ArrayList(); // Number "extends" Number (in this context) List< ? extends Number> foo3 = new ArrayList(); // Integer extends Number List< ? extends Number> foo3 = new ArrayList(); // Double extends Number 
  1. Lesen – Bei den obigen möglichen Zuordnungen, welche Art von Objekt werden Sie garantiert aus der List foo3 lesen:

    • Sie können eine Number lesen, da jede der Listen, die foo3 zugewiesen werden können, eine Number oder eine Unterklasse von Number .
    • Sie können eine foo3 nicht lesen, da foo3 auf eine List .
    • Sie können kein Double lesen, weil foo3 auf eine List .
  2. Schreiben – In Anbetracht der obigen möglichen Zuordnungen, welchen Objekttyp könnten Sie der List foo3 hinzufügen, die für alle oben genannten ArrayList Zuweisungen List foo3 wäre:

    • Sie können keine Integer hinzufügen, weil foo3 auf eine List .
    • Sie können kein Double hinzufügen, da foo3 auf eine List .
    • Sie können keine Number hinzufügen, da foo3 auf eine List .

Sie können kein Objekt zur List< ? extends T> hinzufügen List< ? extends T> List< ? extends T> weil Sie nicht garantieren können, auf welche Art von List es wirklich zeigt, so dass Sie nicht garantieren können, dass das Objekt in dieser List erlaubt ist. Die einzige “Garantie” ist, dass Sie nur davon lesen können und Sie erhalten eine T oder Unterklasse von T

super

Nun betrachte List < ? super T> List < ? super T> .

Die Platzhalterdeklaration von List< ? super Integer> foo3 List< ? super Integer> foo3 bedeutet, dass es sich um legale Zuweisungen handelt:

 List< ? super Integer> foo3 = new ArrayList(); // Integer is a "superclass" of Integer (in this context) List< ? super Integer> foo3 = new ArrayList(); // Number is a superclass of Integer List< ? super Integer> foo3 = new ArrayList(); // Object is a superclass of Integer 
  1. Lesen – Bei den oben genannten möglichen Zuordnungen erhalten Sie beim Lesen aus der List foo3 garantiert, welche Art von Objekt Sie erhalten:

    • Sie haben keine Garantie für Integer da foo3 auf eine List oder List .
    • Sie haben keine Garantie für eine Number da foo3 auf eine List .
    • Die einzige Garantie ist, dass Sie eine Instanz eines Object oder einer Unterklasse von Object (aber Sie wissen nicht, welche Unterklasse).
  2. Schreiben – In Anbetracht der obigen möglichen Zuordnungen, welchen Objekttyp könnten Sie der List foo3 hinzufügen, die für alle oben genannten ArrayList Zuweisungen List foo3 wäre:

    • Sie können eine Integer hinzufügen, da eine Integer in einer der obigen Listen zulässig ist.
    • Sie können eine Instanz einer Unterklasse von Integer hinzufügen, da eine Instanz einer Unterklasse von Integer in einer der obigen Listen zulässig ist.
    • Sie können kein Double hinzufügen, da foo3 auf eine ArrayList .
    • Sie können keine Number hinzufügen, da foo3 auf eine ArrayList .
    • Sie können kein Object hinzufügen, weil foo3 auf eine ArrayList .

PECS

Denken Sie daran, PECS : “Producer erweitert, Verbraucher Super” .

  • “Producer Extends” – Wenn Sie eine List benötigen, um T Werte zu erzeugen (Sie möchten T s aus der Liste lesen), müssen Sie es mit ? extends T ? extends T , zB List< ? extends Integer> List< ? extends Integer> . Sie können diese Liste jedoch nicht hinzufügen.

  • “Consumer Super” – Wenn Sie eine List benötigen, um T Werte zu konsumieren (Sie möchten T s in die Liste schreiben), müssen Sie sie mit ? super T ? super T , zB List< ? super Integer> List< ? super Integer> . Aber es gibt keine Garantie, welche Art von Objekt Sie aus dieser Liste lesen können.

  • Wenn Sie sowohl aus einer Liste lesen als auch in eine Liste schreiben müssen, müssen Sie sie genau deklarieren, ohne Platzhalter zu verwenden, zB List .

Beispiel

Beachten Sie dieses Beispiel aus den Java Generics FAQ . Beachten Sie, dass die Quellenliste src (die Produktionsliste) extends verwendet und die Zielliste dest (die konsumierende Liste) super :

 public class Collections { public static  void copy(List< ? super T> dest, List< ? extends T> src) { for (int i = 0; i < src.size(); i++) dest.set(i, src.get(i)); } } 

Siehe auch Wie kann ich zur Liste hinzufügen? erweitert Nummer> Datenstrukturen?

Stellen Sie sich vor, Sie hätten diese Hierarchie

Bildbeschreibung hier eingeben

1. Erweitert

Durch Schreiben

  List< ? extends C2> list; 

Sie sagen, dass diese list in der Lage sein wird, auf ein Objekt vom Typ (z. B. ArrayList ) zu ArrayList dessen generischer Typ einer der 7 Subtypen von C2 (einschließlich C2 ) ist:

  1. C2: new ArrayList(); , (ein Objekt, das C2 oder Subtypen speichern kann) oder
  2. D1: new ArrayList(); , (ein Objekt, das D1 oder Subtypen speichern kann) oder
  3. D2: new ArrayList(); , (ein Objekt, das D2 oder Subtypen speichern kann) oder …

und so weiter. Sieben verschiedene Fälle:

  1) new ArrayList(): can store C2 D1 D2 E1 E2 E3 E4 2) new ArrayList(): can store D1 E1 E2 3) new ArrayList(): can store D2 E3 E4 4) new ArrayList(): can store E1 5) new ArrayList(): can store E2 6) new ArrayList(): can store E3 7) new ArrayList(): can store E4 

Wir haben eine Reihe von “speicherbaren” Typen für jeden möglichen Fall: 7 (rote) Sätze sind hier grafisch dargestellt

Bildbeschreibung hier eingeben

Wie Sie sehen können, gibt es keinen sicheren Typ , der allen Fällen gemeinsam ist:

  • Sie können nicht list.add(new C2(){}); weil es könnte list = new ArrayList();
  • Sie können nicht list.add(new D1(){}); weil es könnte list = new ArrayList();

und so weiter.

2. Super

Durch Schreiben

  List< ? super C2> list; 

Sie sagen, dass diese list in der Lage sein wird, auf ein Objekt vom Typ (zum Beispiel) ArrayList zu verweisen, dessen generischer Typ einer der 7 Supertypen von C2 ( C2 eingeschlossen):

  • A1: new ArrayList(); , (ein Objekt, das A1 oder Subtypen speichern kann) oder
  • A2: new ArrayList(); , (ein Objekt, das A2 oder Subtypen speichern kann) oder
  • A3: new ArrayList(); , (ein Objekt, das A3 oder Subtypen speichern kann) oder …

und so weiter. Sieben verschiedene Fälle:

  1) new ArrayList(): can store A1 B1 B2 C1 C2 D1 D2 E1 E2 E3 E4 2) new ArrayList(): can store A2 B2 C1 C2 D1 D2 E1 E2 E3 E4 3) new ArrayList(): can store A3 B3 C2 C3 D1 D2 E1 E2 E3 E4 4) new ArrayList(): can store A4 B3 B4 C2 C3 D1 D2 E1 E2 E3 E4 5) new ArrayList(): can store B2 C1 C2 D1 D2 E1 E2 E3 E4 6) new ArrayList(): can store B3 C2 C3 D1 D2 E1 E2 E3 E4 7) new ArrayList(): can store C2 D1 D2 E1 E2 E3 E4 

Wir haben eine Reihe von “speicherbaren” Typen für jeden möglichen Fall: 7 (rote) Sätze sind hier grafisch dargestellt

Bildbeschreibung hier eingeben

Wie Sie sehen können, haben wir hier sieben sichere Typen , die jedem Fall gemeinsam sind: C2 , D1 , D2 , E1 , E2 , E3 , E4 .

  • Sie können list.add(new C2(){}); weil, unabhängig von der Art der Liste, auf die wir verweisen, C2 erlaubt ist
  • Sie können list.add(new D1(){}); weil D1 unabhängig von der Art der Liste, auf die wir verweisen, zulässig ist

und so weiter. Sie haben wahrscheinlich bemerkt, dass diese Typen ab dem Typ C2 der Hierarchie entsprechen.

Anmerkungen

Hier die komplette Hierarchie, wenn Sie einige Tests machen möchten

 interface A1{} interface A2{} interface A3{} interface A4{} interface B1 extends A1{} interface B2 extends A1,A2{} interface B3 extends A3,A4{} interface B4 extends A4{} interface C1 extends B2{} interface C2 extends B2,B3{} interface C3 extends B3{} interface D1 extends C1,C2{} interface D2 extends C2{} interface E1 extends D1{} interface E2 extends D1{} interface E3 extends D2{} interface E4 extends D2{} 

Ich liebe die Antwort von @Bert F, aber so sieht mein Gehirn es.

Ich habe ein X in meiner Hand. Wenn ich mein X in eine Liste schreiben möchte, muss diese Liste entweder eine Liste von X oder eine Liste von Dingen sein, zu denen mein X upcasted werden kann, wenn ich sie schreibe, also jede Oberklasse von X …

 List< ? super X> 

Wenn ich eine Liste erhalte und ich ein X aus dieser Liste lesen möchte, ist das besser eine Liste von X oder eine Liste von Dingen, die zu X hochgestuft werden können, wenn ich sie lese, dh alles, was X erweitert

 List< ? extends X> 

Hoffe das hilft.

Basierend auf Bert Fs Antwort möchte ich mein Verständnis erklären.

Sagen wir, wir haben 3 classn als

 public class Fruit{} public class Melon extends Fruit{} public class WaterMelon extends Melon{} 

Hier haben wir

 List< ? extends Fruit> fruitExtendedList = … //Says that I can be a list of any object as long as this object extends Fruit. 

Ok, jetzt versuchen wir etwas von FruitExtendedList zu bekommen

 Fruit fruit = fruitExtendedList.get(position) //This is valid as it can only return Fruit or its subclass. 

Lass es uns nochmal versuchen

 Melon melon = fruitExtendedList.get(position) //This is not valid because fruitExtendedList can be a list of Fruit only, it may not be //list of Melon or WaterMelon and in java we cannot assign sub class object to //super class object reference without explicitly casting it. 

Gleiches ist der Fall für

 WaterMelon waterMelon = fruitExtendedList.get(position) 

Versuchen wir jetzt, ein Objekt in objectList zu setzen

Obstobjekt hinzufügen

 fruitExtendedList.add(new Fruit()) //This in not valid because as we know fruitExtendedList can be a list of any //object as long as this object extends Fruit. So what if it was the list of //WaterMelon or Melon you cannot add Fruit to the list of WaterMelon or Melon. 

Melon-Objekt hinzufügen

 fruitExtendedList.add(new Melon()) //This would be valid if fruitExtendedList was the list of Fruit but it may //not be, as it can also be the list of WaterMelon object. So, we see an invalid //condition already. 

Abschließend versuchen Sie, WaterMelon Objekt hinzuzufügen

 fruitExtendedList.add(new WaterMelon()) //Ok, we got it now we can finally write to fruitExtendedList as WaterMelon //can be added to the list of Fruit or Melon as any superclass reference can point //to its subclass object. 

Aber was ist, wenn jemand beschließt, eine neue Art von Lemon zu machen, sagen wir für Argumente SaltyLemon als

 public class SaltyLemon extends Lemon{} 

FruitExtendedList kann nun eine Liste von Fruit, Melon, WaterMelon oder SaltyLemon sein.

Also, unsere Aussage

 fruitExtendedList.add(new WaterMelon()) 

ist auch nicht gültig.

Grundsätzlich können wir sagen, dass wir nichts in eine objectList schreiben können.

Dies fasst List< ? extends Fruit> List< ? extends Fruit>

Jetzt sehen wir mal

 List< ? super Melon> melonSuperList= … //Says that I can be a list of anything as long as its object has super class of Melon. 

Jetzt versuchen wir, etwas von melonSuperList zu bekommen

 Fruit fruit = melonSuperList.get(position) //This is not valid as melonSuperList can be a list of Object as in java all //the object extends from Object class. So, Object can be super class of Melon and //melonSuperList can be a list of Object type 

Ähnlich kann Melon, Watermelon oder irgendein anderes Objekt nicht gelesen werden.

Beachten Sie jedoch, dass wir Instanzen des Objekttyps lesen können

 Object myObject = melonSuperList.get(position) //This is valid because Object cannot have any super class and above statement //can return only Fruit, Melon, WaterMelon or Object they all can be referenced by //Object type reference. 

Versuchen wir jetzt, einen Wert von melonSuperList festzulegen.

Objekttyp Objekt hinzufügen

 melonSuperList.add(new Object()) //This is not valid as melonSuperList can be a list of Fruit or Melon. //Note that Melon itself can be considered as super class of Melon. 

Objekt vom Typ Frucht hinzufügen

 melonSuperList.add(new Fruit()) //This is also not valid as melonSuperList can be list of Melon 

Melon-Objekt hinzufügen

 melonSuperList.add(new Melon()) //This is valid because melonSuperList can be list of Object, Fruit or Melon and in //this entire list we can add Melon type object. 

WaterMelon-Objekt hinzufügen

 melonSuperList.add(new WaterMelon()) //This is also valid because of same reason as adding Melon 

Um es zusammenzufassen, können wir Melon oder seine Unterklasse in melonSuperList und nur Objekttyp Objekt hinzufügen.

Super ist eine untere Grenze, und erweitert ist eine obere Grenze.

Laut http://download.oracle.com/javase/tutorial/extra/generics/morefun.html :

Die Lösung besteht darin, eine Form von beschränkter Wildcard zu verwenden, die wir noch nicht gesehen haben: Platzhalter mit einer unteren Grenze. Die Syntax? Super T bezeichnet einen unbekannten Typ, der ein Supertyp von T ist (oder T selbst; denken Sie daran, dass die Supertypbeziehung reflexiv ist). Es ist das duale der begrenzten Wildcards, die wir benutzt haben, wo wir verwenden? erweitert T um einen unbekannten Typ zu bezeichnen, der ein Subtyp von T ist.

Die verwirrendste Sache hier ist, dass unabhängig von den von uns angegebenen Typeinschränkungen die Zuweisung nur auf eine Weise funktioniert:

 baseClassInstance = derivedClassInstance; 

Sie können denken, dass Integer extends Number und dass ein Integer als < ? extends Number> < ? extends Number> , aber der Compiler wird Ihnen sagen, dass < ? extends Number> cannot be converted to Integer < ? extends Number> cannot be converted to Integer (das heißt, im menschlichen Sprachgebrauch ist es falsch, dass alles, was die Zahl erweitert, in Ganzzahl konvertiert werden kann ):

 class Holder { T v; T get() { return v; } void set(T n) { v=n; } } class A { public static void main(String[]args) { Holder< ? extends Number> he = new Holder(); Holder< ? super Number> hs = new Holder(); Integer i; Number n; Object o; // Producer Super: always gives an error except // when consumer expects just Object i = hs.get(); // < ? super Number> cannot be converted to Integer n = hs.get(); // < ? super Number> cannot be converted to Number // < ? super Number> cannot be converted to ... (but // there is no class between Number and Object) o = hs.get(); // Consumer Super hs.set(i); hs.set(n); hs.set(o); // Object cannot be converted to < ? super Number> // Producer Extends i = he.get(); // < ? extends Number> cannot be converted to Integer n = he.get(); o = he.get(); // Consumer Extends: always gives an error he.set(i); // Integer cannot be converted to < ? extends Number> he.set(n); // Number cannot be converted to < ? extends Number> he.set(o); // Object cannot be converted to < ? extends Number> } } 

hs.set(i); ist in Ordnung, weil Integer in jede Oberklasse von Number konvertiert werden kann (und nicht, weil Integer eine Oberklasse von Number , was nicht wahr ist).

EDIT hat einen Kommentar über Consumer Extends und Producer Super hinzugefügt – sie sind nicht sinnvoll, da sie entsprechend nichts und nur Object angeben. Es wird empfohlen, sich an PECS zu erinnern, da CEPS nie nützlich ist.

Mit extends können Sie nur aus der Sammlung erhalten. Du kannst es nicht tun. Auch, obwohl Super erlaubt, sowohl zu bekommen als auch zu setzen, ist der Rückgabetyp während erhalten ? Super T.

Die generischen Platzhalter zielen auf zwei primäre Bedürfnisse ab:

Lesen aus einer generischen Sammlung Einfügen in eine generische Sammlung Es gibt drei Möglichkeiten, eine Sammlung (Variable) mit generischen Platzhaltern zu definieren. Diese sind:

 List< ?> listUknown = new ArrayList(); List< ? extends A> listUknown = new ArrayList(); List< ? super A> listUknown = new ArrayList(); 

List< ?> Bedeutet eine Liste, die in einen unbekannten Typ eingegeben wurde. Dies könnte eine List , eine List , eine List usw. sein.

List< ? extends A> List< ? extends A> bedeutet eine Liste von Objekten, die Instanzen der class A oder subclasses of A (z. B. B und C). List< ? super A> List< ? super A> bedeutet, dass die Liste entweder in die A class oder in eine superclass of A .

Lesen Sie mehr: http://tutorials.jenkov.com/java-generics/wildcards.html

Wann zu verwenden, erweitert und super

Wildcards sind in Methodenparametern am nützlichsten. Sie ermöglichen die notwendige Flexibilität in Methodenschnittstellen.

Die Leute sind oft verwirrt, wenn sie Extents und wann Super-Bounds verwenden. Die Faustregel ist das Get-Put-Prinzip. Wenn Sie etwas aus einem parametrisierten Container erhalten, verwenden Sie extends.

 int totalFuel(List< ? extends Vehicle> list) { int total = 0; for(Vehicle v : list) { total += v.getFuel(); } return total;} 

Die Methode totalFuel ruft Fahrzeuge aus der Liste auf, fragt sie nach der Menge an Treibstoff und berechnet die Gesamtsumme. Wenn Sie Objekte in einen parametrisierten Container einfügen, verwenden Sie super.

 int totalValue(Valuer< ? super Vehicle> valuer) { int total = 0; for(Vehicle v : vehicles) { total += valuer.evaluate(v); } return total;} 

Die Methode totalValue setzt Fahrzeuge in den Valuer ein. Es ist nützlich zu wissen, dass erweiterte Grenzen viel häufiger sind als super.

Sie können alle obigen Antworten durchgehen, um zu verstehen, warum die .add() auf '< ?>' , '< ? extends>' '< ? extends>' und teilweise auf '< ? extends>' '< ? super>' '< ? super>' .

Aber hier ist das Fazit von allem, wenn Sie sich daran erinnern wollen und nicht jedes Mal die Antwort erforschen wollen:

List< ? extends A> List< ? extends A> bedeutet, dass dies jede List von A und Unterklasse von A akzeptiert. Aber Sie können dieser Liste nichts hinzufügen. Nicht einmal Objekte vom Typ A

List< ? super A> List< ? super A> bedeutet, dass dies jede Liste von A und Superklasse von A akzeptiert. Sie können Objekte des Typs A und ihrer Unterklassen hinzufügen.

Liste < ? extends X> erlaubt nicht, etwas in die Liste einzufügen.

Liste < ? Super X> erlaubt alles hinzuzufügen, was ein X (X oder seine Unterklasse) ist.

Die Abstimmungsergebnisse decken die Details zu vielen Aspekten ab. Ich würde jedoch versuchen, dies auf andere Weise zu beantworten.

Es gibt zwei Dinge, die wir beachten müssen,

1. Zuordnung zur Listenvariablen

List< ? extends X> listvar;

Hier kann jede Liste von X oder Liste von Unterklassen von X zu listvar zugewiesen werden.

List< ? extends Number> listvar; listvar = new ArrayList(); listvar = new ArrayList();


List< ? super X> listvar;

Hier kann jede Liste von X oder Liste von Oberklassen von X zu listvar zugewiesen werden.

List< ? super Number> listvar; listvar = new ArrayList(); listvar = new ArrayList();

2. Führen Sie eine Lese- oder Schreiboperation für die Listenvariable durch

 `List< ? extends X> listvar;` 

Sie können diese function verwenden, um eine Liste in Methodenargumenten zu akzeptieren und alle Operationen am Typ X auszuführen (Hinweis: Sie können nur Objekte vom Typ X aus der Liste lesen ).

 `List< ? super Number> listvar; 

Sie können diese function verwenden, um eine Liste in Methodenargumenten zu akzeptieren und Operationen für den Typ Object auszuführen. Sie können nur Objekte des Typs Object aus der Liste lesen . Aber ja, zusätzliche Sache hier ist, können Sie Objekte des Typs X in die Liste hinzufügen.

  | v 

Liste < ? erweitert X> ? kann entweder eine Unterklasse von X oder selbst X .

Liste < ? Super X> ? kann entweder jede Oberklasse von X oder selbst X .

  ^ | 

Beispiel, Reihenfolge der inheritance wird angenommen als O> S> T> U> V

Verwenden von erweitertem Schlüsselwort,

Richtig:

 List< ? extends T> Object = new List(); List< ? extends T> Object = new List(); List< ? extends T> Object = new List(); 

Falsch:

 List< ? extends T> Object = new List(); List< ? extends T> Object = new List(); 

super Schlagwort:

Richtig:

 List< ? super T> Object = new List(); List< ? super T> Object = new List(); List< ? super T> Object = new List(); 

Falsch:

 List< ? super T> Object = new List(); List< ? super T> Object = new List(); 

Objekt hinzufügen: Listenobjekt = neue Liste ();

 Object.add(new T()); //error 

Aber warum Fehler? Schauen wir uns die Möglichkeiten der Initialisierung von List Object an

 List< ? extends T> Object = new List(); List< ? extends T> Object = new List(); List< ? extends T> Object = new List(); 

Wenn wir Object.add verwenden (new T ()); dann wird es nur richtig sein, wenn

 List< ? extends T> Object = new List(); 

Aber es gibt zwei zusätzliche Möglichkeiten

Listenobjekt = neue Liste (); Listenobjekt = neue Liste (); Wenn wir versuchen, den obigen zwei Möglichkeiten (new T ()) hinzuzufügen, wird es einen Fehler geben, weil T die höhere class von U und V ist. Wir versuchen, ein T-Objekt [das ist (new T ())] zu einer Liste vom Typ U und V hinzuzufügen. Höheres classnobjekt (Basisklasse) kann nicht an Unterklassenobjekt (Unterklasse) übergeben werden.

Aufgrund der zusätzlichen zwei Möglichkeiten, gibt Java Ihnen Fehler, selbst wenn Sie die richtige Möglichkeit als Java verwenden, wissen Sie nicht, auf welches Objekt Sie verweisen. So können Sie keine Objekte zum Listenobjekt hinzufügen = new List (); da es Möglichkeiten gibt, die nicht gültig sind.

Objekt hinzufügen: Listenobjekt = neue Liste ();

 Object.add(new T()); // compiles fine without error Object.add(new U()); // compiles fine without error Object.add(new V()); // compiles fine without error Object.add(new S()); // error Object.add(new O()); // error 

Aber warum tritt ein Fehler in den obigen zwei auf? wir können Object.add verwenden (new T ()); nur auf die folgenden Möglichkeiten,

 List< ? super T> Object = new List(); List< ? super T> Object = new List(); List< ? super T> Object = new List(); 

Wenn wir versucht haben, Object.add (new T ()) in ListObject = new List () zu verwenden; und Listenobjekt = neue Liste (); dann wird es einen Fehler geben Dies liegt daran, dass wir kein T-Objekt [welches ein neues T ()] ist, zu dem Listen-Objekt = new List () hinzufügen können; weil es ein Objekt vom Typ U ist. Wir können kein T-Objekt [welches ein neues T ()] zu U Object hinzufügen, weil T eine Basisklasse und U eine Subklasse ist. Wir können der Unterklasse keine Basisklasse hinzufügen, und deshalb tritt ein Fehler auf. Dies gilt auch für den anderen Fall.