Wie kann ich zur Liste <hinzufügen? erweitert Anzahl> Datenstrukturen?

152

Ich habe eine Liste, die wie folgt deklariert ist:

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

Ich habe versucht, foo3 3 hinzuzufügen. Ich erhalte jedoch eine Fehlermeldung wie folgt:

The method add(capture#1-of ? extends Number) in the type List<capture#1-of ?
extends Number> is not applicable for the arguments (ExtendsNumber)
unj2
quelle
61
Beachten Sie, dass List<? extends Number>dies nicht "Liste von Objekten unterschiedlichen Typs, die sich alle erstrecken Number" bedeutet. Es bedeutet "Liste von Objekten eines einzelnen Typs, die sich erweitern Number".
Pavel Minaev
1
Sie überprüfen besser die PECS-Regel, Produzent erweitert, Verbraucher super. stackoverflow.com/questions/2723397/…
noego

Antworten:

301

Entschuldigung, aber du kannst nicht.

Die Platzhalterdeklaration von List<? extends Number> foo3bedeutet, dass die Variable foo3einen beliebigen Wert aus einer Typenfamilie enthalten kann (anstelle eines beliebigen Werts eines bestimmten Typs). Dies bedeutet, dass es sich bei diesen um rechtliche Aufträge handelt:

List<? extends Number> foo3 = new ArrayList<Number>;  // Number "extends" Number
List<? extends Number> foo3 = new ArrayList<Integer>; // Integer extends Number
List<? extends Number> foo3 = new ArrayList<Double>;  // Double extends Number

List foo3Vor diesem Hintergrund wäre es nach einer der oben genannten möglichen ArrayListZuweisungen legal , welche Art von Objekt Sie hinzufügen könnten :

  • Sie können keine hinzufügen, Integerda foo3dies auf eine zeigen könnte List<Double>.
  • Sie können kein hinzufügen, Doubleda foo3dies auf ein zeigen könnte List<Integer>.
  • Sie können kein Numberweil hinzufügenfoo3dies auf ein zeigen könnte List<Integer>.

Sie können kein Objekt hinzufügen, List<? extends T>da Sie nicht garantieren können, auf welche Art von Objekt Listes wirklich zeigt. Sie können also nicht garantieren, dass das Objekt darin zulässig istList . Die einzige "Garantie" ist, dass Sie nur daraus lesen können und eine Toder eine Unterklasse von erhalten T.

Die umgekehrte Logik gilt superzList<? super T> . Diese sind legal:

List<? super Number> foo3 = new ArrayList<Number>; // Number is a "super" of Number
List<? super Number> foo3 = new ArrayList<Object>; // Object is a "super" of Number

Sie können den spezifischen Typ T (z. B. Number) nicht lesen, List<? super T>da Sie nicht garantieren können, auf welche Art von Typ T Listwirklich zeigt. Die einzige "Garantie", die Sie haben, ist, dass Sie einen Wert vom Typ hinzufügen könnenT (oder eine Unterklasse von T) ohne die Integrität der Liste zu verletzen, auf die verwiesen wird.


Das perfekte Beispiel dafür ist die Signatur für Collections.copy():

public static <T> void copy(List<? super T> dest,List<? extends T> src)

Beachten Sie, wie die srcListendeklaration extendses mir ermöglicht, eine Liste aus einer Familie verwandter Listentypen zu übergeben und dennoch zu gewährleisten, dass Werte vom Typ T oder Unterklassen von T erzeugt werden. Sie können der srcListe jedoch keine Werte hinzufügen .

Das dest Listendeklaration superermöglicht es mir, eine Liste aus einer Familie verwandter Listentypen zu übergeben und trotzdem zu garantieren, dass ich einen Wert eines bestimmten Typs T in diese Liste schreiben kann. Es kann jedoch nicht garantiert werden, dass die Werte des bestimmten Typs T gelesen werden, wenn ich aus der Liste lese.

Dank generischer Platzhalter kann ich jetzt jeden dieser Aufrufe mit dieser einzigen Methode ausführen:

// copy(dest, src)
Collections.copy(new ArrayList<Number>(), new ArrayList<Number());
Collections.copy(new ArrayList<Number>(), new ArrayList<Integer());
Collections.copy(new ArrayList<Object>(), new ArrayList<Number>());
Collections.copy(new ArrayList<Object>(), new ArrayList<Double());

Betrachten Sie diesen verwirrenden und sehr breiten Code, um Ihr Gehirn zu trainieren. Die auskommentierten Zeilen sind illegal und der Grund dafür ist ganz rechts in der Zeile angegeben (Sie müssen scrollen, um einige davon zu sehen):

  List<Number> listNumber_ListNumber  = new ArrayList<Number>();
//List<Number> listNumber_ListInteger = new ArrayList<Integer>();                    // error - can assign only exactly <Number>
//List<Number> listNumber_ListDouble  = new ArrayList<Double>();                     // error - can assign only exactly <Number>

  List<? extends Number> listExtendsNumber_ListNumber  = new ArrayList<Number>();
  List<? extends Number> listExtendsNumber_ListInteger = new ArrayList<Integer>();
  List<? extends Number> listExtendsNumber_ListDouble  = new ArrayList<Double>();

  List<? super Number> listSuperNumber_ListNumber  = new ArrayList<Number>();
//List<? super Number> listSuperNumber_ListInteger = new ArrayList<Integer>();      // error - Integer is not superclass of Number
//List<? super Number> listSuperNumber_ListDouble  = new ArrayList<Double>();       // error - Double is not superclass of Number


//List<Integer> listInteger_ListNumber  = new ArrayList<Number>();                  // error - can assign only exactly <Integer>
  List<Integer> listInteger_ListInteger = new ArrayList<Integer>();
//List<Integer> listInteger_ListDouble  = new ArrayList<Double>();                  // error - can assign only exactly <Integer>

//List<? extends Integer> listExtendsInteger_ListNumber  = new ArrayList<Number>(); // error - Number is not a subclass of Integer
  List<? extends Integer> listExtendsInteger_ListInteger = new ArrayList<Integer>();
//List<? extends Integer> listExtendsInteger_ListDouble  = new ArrayList<Double>(); // error - Double is not a subclass of Integer

  List<? super Integer> listSuperInteger_ListNumber  = new ArrayList<Number>();
  List<? super Integer> listSuperInteger_ListInteger = new ArrayList<Integer>();
//List<? super Integer> listSuperInteger_ListDouble  = new ArrayList<Double>();     // error - Double is not a superclass of Integer


  listNumber_ListNumber.add(3);             // ok - allowed to add Integer to exactly List<Number>

  // These next 3 are compile errors for the same reason:
  // You don't know what kind of List<T> is really
  // being referenced - it may not be able to hold an Integer.
  // You can't add anything (not Object, Number, Integer,
  // nor Double) to List<? extends Number>      
//listExtendsNumber_ListNumber.add(3);     // error - can't add Integer to *possible* List<Double>, even though it is really List<Number>
//listExtendsNumber_ListInteger.add(3);    // error - can't add Integer to *possible* List<Double>, even though it is really List<Integer>
//listExtendsNumber_ListDouble.add(3);     // error - can't add Integer to *possible* List<Double>, especially since it is really List<Double>

  listSuperNumber_ListNumber.add(3);       // ok - allowed to add Integer to List<Number> or List<Object>

  listInteger_ListInteger.add(3);          // ok - allowed to add Integer to exactly List<Integer> (duh)

  // This fails for same reason above - you can't
  // guarantee what kind of List the var is really
  // pointing to
//listExtendsInteger_ListInteger.add(3);   // error - can't add Integer to *possible* List<X> that is only allowed to hold X's

  listSuperInteger_ListNumber.add(3);      // ok - allowed to add Integer to List<Integer>, List<Number>, or List<Object>
  listSuperInteger_ListInteger.add(3);     // ok - allowed to add Integer to List<Integer>, List<Number>, or List<Object>
Bert F.
quelle
11
Warum dürfen wir einen Satz wie "Liste" schreiben? erweitert Number> foo3 = new ArrayList <Integer> (); `wenn wir kein Element mehr in diese Liste aufnehmen können?
Amit Kumar Gupta
7
@articlestack: Das Hinzufügen ist nicht die einzige nützliche Sache, die mit einer Liste zu tun hat. Sobald eine Liste ausgefüllt ist, kann das Lesen aus einer Liste hilfreich sein. Generische Platzhalter ermöglichen das Schreiben von Code, der generisch für eine Listenfamilie funktioniert, z. B. für List <Integer> oder List <Double>. Sehen Sie sich beispielsweise die Signatur von Collection.copy () an. Das src ListArgument wird extendszum Lesen aus der src-Liste verwendet, während das dest ListArgument superzum Schreiben in die Zielliste verwendet wird. Dies ermöglicht eine Methode, die von List<Integer>oder List<Double>in List<Number>oder kopieren kann List<Object>.
Bert F
Bert F, Entschuldigung für einen späten Kommentar. Gibt es einen Tippfehler in Ihrer Passage, "um einen Wert vom Typ T (oder Unterklasse von T) hinzuzufügen" sollte lauten (oder Superklasse von T)?
Vortex
Ja, ich habe es auch gesehen @Vortex Ich bin mir nicht sicher, ob es richtig ist oder ich es falsch gelesen habe.
Ungeheuer
1
@Vortex - or subclass of Tist richtig. Zum Beispiel kann ich kein an Object(Superklasse von Number) hinzufügen , List<? super Number> foo3da foo3möglicherweise Folgendes zugewiesen wurde: List<? super Number> foo3 = new ArrayList<Number>(das nur Numberoder Unterklassen von enthalten kann Number). <? super Number>bezieht sich auf die Arten von List<>s, die zugewiesen werden können foo3- nicht auf die Arten von Dingen, die hinzugefügt / daraus gelesen werden können. Die Arten von Dingen, die hinzugefügt / entfernt foo3werden können, müssen Dinge sein, die zu jeder Art von Dingen hinzugefügt / entfernt werden List<>können, denen zugewiesen werden kann foo3.
Bert F
17

Sie können nicht (ohne unsichere Würfe). Sie können nur von ihnen lesen.

Das Problem ist, dass Sie nicht genau wissen, von welcher Liste die Liste ist. Es kann sich um eine Liste einer beliebigen Unterklasse von Number handeln. Wenn Sie also versuchen, ein Element darin einzufügen, wissen Sie nicht, dass das Element tatsächlich in die Liste passt.

Zum Beispiel könnte die Liste eine Liste von Bytes sein, also wäre es ein Fehler, ein Floatin sie zu setzen .

sepp2k
quelle
2

"List '<'? Extend Number> ist eigentlich ein Platzhalter für die obere Schranke!

Der Platzhalter mit der oberen Grenze besagt, dass jede Klasse, die Number oder Number selbst erweitert, als formaler Parametertyp verwendet werden kann: Das Problem ergibt sich aus der Tatsache, dass Java nicht weiß, welcher Typ List wirklich ist. Es muss ein GENAUER und EINZIGARTIGER Typ sein. Ich hoffe, es hilft :)

andygro
quelle
1

Sie könnten dies stattdessen tun:

  List<Number> foo3 = new ArrayList<Number>();      
  foo3.add(3);
Ryan Elkins
quelle
1

Es war verwirrend für mich, obwohl ich hier Antworten gelesen habe, bis ich den Kommentar von Pavel Minaev fand:

Beachten Sie, dass Liste <? erweitert Nummer> bedeutet nicht "Liste von Objekten unterschiedlichen Typs, die alle die Nummer erweitern". Es bedeutet "Liste von Objekten eines einzelnen Typs, die Number erweitert"

Danach konnte ich BertF tolle Erklärung verstehen. Liste <? erweitert Anzahl> bedeutet? Es kann sich um einen beliebigen Typ handeln, der Number erweitert (Integer, Double usw.) und in der Deklaration (List <? erweitert Number> list) nicht klarstellt , um welche von ihnen es sich handelt. Wenn Sie also die add-Methode verwenden möchten, ist nicht bekannt, ob die Eingabe erfolgt vom gleichen Typ oder nicht; Was ist der Typ überhaupt?

Also die Elemente von List <? erweitert Nummer> konnte nur beim Konstruieren gesetzt werden.

Beachten Sie auch Folgendes: Wenn wir Vorlagen verwenden, teilen wir dem Compiler mit, mit welchem ​​Typ wir herumspielen. T zum Beispiel hält diesen Typ für uns, aber nicht ? macht das gleiche

Ich muss sagen .. Dies ist einer der schmutzigen zu erklären / lernen

navid
quelle
-1

Wenn Sie die Liste von 'Objekt' erweitern, können Sie list.add verwenden, und wenn Sie list.get verwenden möchten, müssen Sie nur das Objekt in Ihr Objekt umwandeln.

abbasalim
quelle