J'ai une liste qui est déclarée comme ceci:
List<? extends Number> foo3 = new ArrayList<Integer>();
J'ai essayé d'ajouter 3 à foo3. Cependant, je reçois un message d'erreur comme celui-ci:
The method add(capture#1-of ? extends Number) in the type List<capture#1-of ?
extends Number> is not applicable for the arguments (ExtendsNumber)
Désolé, mais vous ne pouvez pas.
La déclaration générique de List<? extends Number> foo3
signifie que la variable foo3
peut contenir toute valeur d'une famille de types (plutôt que toute valeur d'un type spécifique). Cela signifie que toutes ces tâches sont légales:
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
Donc, compte tenu de cela, quel type d’objet pourriez-vous ajouter à List foo3
qui serait légal après l’une quelconque des assignations ArrayList
possibles ci-dessus:
Integer
car foo3
pourrait pointer sur un List<Double>
.Double
car foo3
pourrait pointer sur List<Integer>
.Number
car foo3
pourrait pointer sur List<Integer>
. Vous ne pouvez ajouter aucun objet à List<? extends T>
car vous ne pouvez pas garantir le type de variable List
vers lequel elle pointe réellement. Vous ne pouvez donc pas garantir que l'objet est autorisé dans cette variable List
. La seule "garantie" est que vous ne pouvez que lire et que vous obtenez une T
ou une sous-classe de T
.
La logique inverse s'applique à super
, par ex. List<? super T>
. Ceux-ci sont légaux:
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
Vous ne pouvez pas lire le type spécifique T (par exemple, Number
) à partir de List<? super T>
car vous ne pouvez pas garantir le type de List
sur lequel elle pointe vraiment. La seule "garantie" dont vous disposez est que vous pouvez ajouter une valeur de type T
(ou toute sous-classe de T
) sans porter atteinte à l'intégrité de la liste pointée.
L'exemple parfait est la signature de Collections.copy()
:
public static <T> void copy(List<? super T> dest,List<? extends T> src)
Remarquez comment la déclaration de liste src
utilise extends
pour me permettre de passer toute liste d'une famille de types de liste liés tout en garantissant qu'elle produira des valeurs de type T ou de sous-classes de T. Toutefois, vous ne pouvez pas ajouter à la liste src
.
La déclaration de liste dest
utilise super
pour me permettre de transmettre toute liste d'une famille de types de liste liés tout en garantissant que je peux écrire une valeur d'un type spécifique T dans cette liste. Mais il ne peut pas être garanti de lire les valeurs de spécifique type T si je lis dans la liste.
Alors maintenant, grâce aux caractères génériques génériques, je peux faire n'importe lequel de ces appels avec cette seule méthode:
// 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());
Considérez ce code confus et très large pour exercer votre cerveau. Les lignes commentées sont illégales et la raison en est indiquée à l'extrême droite de la ligne (il faut faire défiler pour en voir certaines):
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>
Vous ne pouvez pas (sans lancer dangereux). Vous pouvez seulement lire d'eux.
Le problème est que vous ne savez pas ce que la liste est exactement. Il peut s'agir d'une liste de n'importe quelle sous-classe de Number. Ainsi, lorsque vous essayez d'y insérer un élément, vous ne savez pas si cet élément entre réellement dans la liste.
Par exemple, la liste peut être une liste de Byte
s. Il serait donc erroné d'y mettre un Float
.
Vous pouvez le faire à la place:
List<Number> foo3 = new ArrayList<Number>();
foo3.add(3);
où expand list from 'Object', vous pouvez utiliser list.add et lorsque vous le souhaitez, list.get n'a besoin que de convertir Object en votre Object;
Cela m'a dérouté même si j'ai lu les réponses ici, jusqu'à ce que je trouve le commentaire de Pavel Minaev:
Notez que List <? extend Number> ne signifie pas "liste d'objets de différents types, qui sont tous étendus à Number". Cela signifie "liste d'objets D'un seul type qui étend Number"
Après cela, j'ai pu comprendre l'explication géniale de BertF. Liste <? étend Nombre> signifie? pourrait être de n'importe quel type (nombre entier, double, etc.) et sa déclaration non clarifiée dans (liste <? extend Number> list), lequel il est, donc quand vous voulez utiliser la méthode add, il est inconnu si l'entrée est du même type ou pas; quel est le type du tout?
Donc, les éléments de la liste <? extend Number> ne peut être défini que lors de la construction.
Notez également ceci: lorsque nous utilisons des modèles, nous disons au compilateur quel type nous gênons.Tpar exemple, conserve ce type pour nous, mais pas? fait la même chose
Je dois dire .. Ceci est l'un des sales pour expliquer/apprendre
"List '<'? Extend Number> est en fait un caractère générique supérieur!
Le caractère générique de limite supérieure indique que toute classe qui étend Number ou Number peut être utilisée comme type de paramètre formel: Le problème provient du fait que Java ne sait pas quel type List est vraiment . Il doit s'agir d'un type EXACT et UNIQUE. J'espère que ça aide :)