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)
List<? extends Number>
cela ne signifie pas "liste d'objets de différents types, qui s'étendent tousNumber
". Cela signifie "liste d'objets d'un seul type qui s'étendNumber
".Réponses:
Désolé, mais vous ne pouvez pas.
La déclaration générique de
List<? extends Number> foo3
signifie que la variablefoo3
peut contenir n'importe quelle valeur d'une famille de types (plutôt que n'importe quelle valeur d'un type spécifique). Cela signifie que l'une de ces cessions est légale:Donc, étant donné cela, quel type d'objet pourriez-vous ajouter à
List foo3
cela serait légal après l'une desArrayList
affectations possibles ci-dessus :Integer
car ilfoo3
pourrait pointer vers un fichierList<Double>
.Double
car ilfoo3
pourrait pointer vers un fichierList<Integer>
.Number
car ilfoo3
pourrait pointer vers un fichierList<Integer>
.Vous ne pouvez pas ajouter d'objet,
List<? extends T>
car vous ne pouvez pas garantir le typeList
auquel il pointe réellement, vous ne pouvez donc pas garantir que l'objet est autorisé dans celaList
. La seule "garantie" est que vous ne pouvez lire qu'à partir de celui-ci et que vous obtiendrez uneT
ou une sous-classe deT
.La logique inverse s'applique à
super
, par exempleList<? super T>
. Ceux-ci sont légaux:Vous ne pouvez pas lire le type spécifique T (par exemple
Number
) à partir deList<? super T>
parce que vous ne pouvez pas garantir quel typeList
il pointe réellement. La seule «garantie» dont vous disposez est que vous pouvez ajouter une valeur de typeT
(ou toute sous-classe deT
) sans violer l'intégrité de la liste pointée.L'exemple parfait en est la signature pour
Collections.copy()
:Remarquez comment la
src
déclaration de liste utiliseextends
pour me permettre de passer n'importe quelle liste d'une famille de types de liste liés tout en garantissant qu'elle produira des valeurs de type T ou des sous-classes de T. Mais vous ne pouvez pas ajouter à lasrc
liste.La
dest
déclaration de liste utilisesuper
pour me permettre de transmettre n'importe quelle liste d'une famille de types de liste associés et garantir toujours que je peux écrire une valeur d'un type T spécifique à cette liste. Mais il ne peut pas être garanti de lire les valeurs de type T spécifique si je lis dans la liste.Alors maintenant, grâce aux jokers génériques, je peux faire n'importe lequel de ces appels avec cette seule méthode:
Considérez ce code déroutant et très large pour exercer votre cerveau. Les lignes commentées sont illégales et la raison pour laquelle est indiquée à l'extrême droite de la ligne (besoin de faire défiler pour voir certaines d'entre elles):
la source
src
List
argument utiliseextends
pour lire à partir de la liste src, tandis que l'dest
List
argument utilisesuper
pour écrire dans la liste dest. Cela permet une méthode qui peut copier depuisList<Integer>
ouList<Double>
versList<Number>
ouList<Object>
.or subclass of T
est correct. Par exemple, je ne peux pas ajouter uneObject
(superclasse deNumber
) àList<? super Number> foo3
carfoo3
peut avoir été assignée comme:List<? super Number> foo3 = new ArrayList<Number>
(qui ne peut contenir queNumber
ou des sous-classes deNumber
).<? super Number>
fait référence aux types deList<>
s qui peuvent être assignésfoo3
- pas aux types de choses qui peuvent être ajoutées / lues à partir de celui-ci. Les types de choses qui peuvent être ajoutées / suppriméesfoo3
doivent être des choses qui peuvent être ajoutées / supprimées de tout type de ceList<>
qui peut être attribuéfoo3
.Vous ne pouvez pas (sans lancers dangereux). Vous ne pouvez lire que d'eux.
Le problème est que vous ne savez pas exactement de quoi la liste est une liste. Cela pourrait être une liste de n'importe quelle sous-classe de Number, donc lorsque vous essayez d'y mettre un élément, vous ne savez pas que l'élément s'inscrit réellement dans la liste.
Par exemple, la liste peut être une liste de
Byte
s, donc ce serait une erreur d'y mettre unFloat
.la source
"List '<'? Extend Number> est en fait un caractère générique de limite supérieure!
Le caractère générique à limite supérieure indique que toute classe qui étend Number ou Number lui-même peut être utilisée comme type de paramètre formel: Le problème vient du fait que Java ne sait pas quel type est réellement List . Il doit s'agir d'un type EXACT et UNIQUE. J'espère que cela aide :)
la source
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:
Après cela, j'ai pu comprendre l'explication géniale de BertF. Liste <? étend le nombre> signifie? pourrait être de n'importe quel type étendant Number (Integer, Double, etc.) et il n'est pas clarifié dans la déclaration (List <? extended Number> list) celui qui d'entre eux il est, donc quand vous voulez utiliser la méthode add, on ne sait pas si l'entrée est du même type ou non; quel est le type du tout?
Donc, les éléments de List <? extend Number> ne pouvait être défini que lors de la construction.
Notez également ceci: lorsque nous utilisons des modèles, nous indiquons au compilateur quel type nous manipulons. T par exemple détient ce type pour nous, mais pas ? fait la même chose
Je dois dire ... C'est l'un des sales à expliquer / apprendre
la source
Vous pouvez le faire à la place:
la source
Vous pouvez le fudger en créant une référence à la liste avec un type différent.
(Ce sont les "lancers non sécurisés" mentionnés par sepp2k.)
Parce que
untypedList
,superclassedList
et netrulyclassedList
sont que des références àlist
, vous ajouterez toujours des éléments à la ArrayList d'origine.Vous n'avez pas besoin de l'utiliser
(List<?>)
dans l'exemple ci-dessus, mais vous en aurez peut-être besoin dans votre code, selon le type de code quilist
vous a été donné.Notez que l'utilisation
?
vous donnera des avertissements du compilateur, jusqu'à ce que vous mettiez ceci au-dessus de votre fonction:la source
où étendre la liste à partir de 'Object', vous pouvez utiliser list.add et quand vous le souhaitez, utilisez list.get seulement besoin de convertir Object en votre Object;
la source