Il est connu que arraylist init. devrait être comme ça
ArrayList<A> a = new ArrayList<A>();
ArrayList<Integer> a = new ArrayList<Number>(); // compile-time error
alors, pourquoi Java les autorise-t-il?
1. ArrayList<? extends Object> a1 = new ArrayList<Object>();
2. ArrayList<?> a2 = new ArrayList<Integer>();
alors, s'ils sont corrects, pourquoi ne pas les permettre?
1. a1.add(3);
2. a2.add(3);
le message du compilateur est le suivant: La méthode add (int, capture # 1-of? extend Object) dans le type ArrayList n'est pas applicable pour les arguments (int)
plus général
1. a1.add(null e);
2. a2.add(? e);
J'ai lu des articles à ce sujet, mais je suis ravi de vous entendre. Merci
l'autre point amusant est:
ArrayList<ArrayList<?>> a = new ArrayList<ArrayList<?>>(); // correct
ArrayList<?> a = new ArrayList<?>(); // wrong. I know it's reason but I have some
question in my mind that mentioned above
Vous ne pouvez pas affecter un List<Number>
à une référence de type List<Integer>
car List<Number>
autorise des types de nombres autres que Integer
. Si vous étiez autorisé à le faire, les éléments suivants seraient autorisés:
List<Number> numbers = new ArrayList<Number>();
numbers.add(1.1); // add a double
List<Integer> ints = numbers;
Integer fail = ints.get(0); // ClassCastException!
Le type List<Integer>
garantit que tout ce qu’il contient sera une Integer
. C'est pourquoi vous êtes autorisé à en extraire une Integer
sans casting. Comme vous pouvez le constater, si le compilateur autorisait l'affectation d'une List
d'un autre type, tel que Number
à un List<Integer>
, cette garantie serait rompue.
Assigner un List<Integer>
à une référence d'un type tel que List<?>
ou List<? extends Number>
est légal car le ?
signifie "un sous-type inconnu du type donné" (où le type est Object
dans le cas de ?
et Number
dans le cas de ? extends Number
.
Puisque ?
indique que vous ne savez pas quel type d'objet la List
va accepter, il n'est pas légal d'ajouter quoi que ce soit d'autre que null
. Vous êtes cependant autorisé à récupérer tout objet, ce qui a pour but d'utiliser un type de caractère générique lié à ? extends X
. Notez que l'inverse est vrai pour un type de caractère générique lié ? super X
... un List<? super Integer>
est "une liste d'un type inconnu qui est au moins un supertype de Integer
". Bien que vous ne sachiez pas exactement de quel type de List
il s'agit (il pourrait s'agir de List<Integer>
, List<Number>
, List<Object>
), vous savez avec certitude que quoi que ce soit, une Integer
peut y être ajoutée.
Enfin, new ArrayList<?>()
n’est pas légal car lorsque vous créez une instance d’une classe paramétrée telle que ArrayList
, vous devez fournir un paramètre de type spécifique. Vous pouvez vraiment utiliser ce que vous voulez dans votre exemple (Object
, Foo
, peu importe), car vous ne pourrez jamais rien y ajouter que null
puisque vous l'attribuez directement à une référence ArrayList<?>
.
La clé réside dans les différences entre les références et les instances et ce que la référence peut promettre et ce que l’instance peut réellement faire.
ArrayList<A> a = new ArrayList<A>();
Ici, a
est une référence à une instance d'un type spécifique - exactement une liste de tableaux de A
s. Plus explicitement, a
est une référence à une liste de tableaux qui acceptera A
s et produira A
s. new ArrayList<A>()
est une instance d'une liste de tableaux de A
s, c'est-à-dire une liste de tableaux qui acceptera A
s et produira A
s.
ArrayList<Integer> a = new ArrayList<Number>();
Ici, a
est une référence à exactement une liste de tableaux de Integers
, c’est-à-dire exactement une liste de tableaux pouvant accepter Integer
s et produisant Integer
s. Il ne peut pas pointer sur une liste de tableaux de Number
s. Cette liste de Number
s ne peut pas respecter toutes les promesses de ArrayList<Integer> a
(c’est-à-dire qu’une liste de Number
s peut produire des objets qui ne sont pas Integer
s, même s’il est vide à ce moment-là).
ArrayList<Number> a = new ArrayList<Integer>();
Ici, la déclaration de a
indique que a
fera référence à une liste de tableaux de Number
s, c’est-à-dire à une liste de tableaux qui acceptera Number
s et produira Number
s. Il ne peut pas pointer sur une liste de tableaux de Integer
s, car la déclaration de type de a
indique que a
peut accepter n'importe quel Number
, mais que cette liste de Integer
s ne peut accepter aucun Number
, mais uniquement Integer
s.
ArrayList<? extends Object> a= new ArrayList<Object>();
Ici, a
est une référence (générique) à une famille de types plutôt qu’une référence à un type spécifique. Il peut pointer sur n'importe quelle liste appartenant à cette famille. Cependant, le compromis pour cette référence flexible Nice est qu’ils ne peuvent pas promettre toutes les fonctionnalités qu’il aurait pu s’il s’agissait d’une référence spécifique à un type (par exemple, non générique). Dans ce cas, a
est une référence à une liste de tableaux qui va produireObject
s. Mais, contrairement à une référence de liste spécifique à un type, cette référence a
ne peut pas accepter aucun Object
. (c’est-à-dire que tous les membres de la famille de types que a
ne peut pas pointer ne peuvent accepter aucun Object
, par exemple une liste de tableaux de Integer
s ne peut accepter que des Integer
s.)
ArrayList<? super Integer> a = new ArrayList<Number>();
Encore une fois, a
est une référence à une famille de types (plutôt qu’à un type spécifique). Étant donné que le caractère générique utilise super
, cette référence à la liste peut accepter Integer
s, mais elle ne peut pas produire Integer
s. En d'autres termes, nous savons que tout membre de la famille des types que a
peut désigner peut accepter un Integer
. Cependant, tous les membres de cette famille ne peuvent pas produire Integer
s.
PECS - Producteur extends
, consommateur super
- Ce mnémonique vous rappelle que l'utilisation de extends
signifie que le type générique peut produire le type spécifique (mais ne peut pas l'accepter). Utiliser super
signifie que le type générique peut consommer _ (accepter) le type spécifique (mais ne peut pas le produire).
ArrayList<ArrayList<?>> a
Une liste de tableaux contenant des références à toute liste membre d'une famille de types de listes de tableaux.
= new ArrayList<ArrayList<?>>(); // correct
Une instance d'une liste de tableaux qui contient des références à toute liste membre d'une famille de types de listes de tableaux.
ArrayList<?> a
Une référence à une liste de tableaux (membre de la famille des types de liste de tableaux).
= new ArrayList<?>()
ArrayList<?>
fait référence à n'importe quel type d'une famille de types de liste de tableaux, mais vous ne pouvez instancier qu'un type spécifique.
Voir aussi Comment puis-je ajouter à la liste <? étend Nombre> structures de données?
Vous avez des attentes étranges. Si vous donniez la chaîne d'arguments qui vous y ont conduit, nous pourrions en repérer le défaut. En l'état actuel des choses, je ne peux vous donner qu'un bref aperçu des médicaments génériques, dans l'espoir d'aborder les points que vous pourriez avoir mal compris.
ArrayList<? extends Object>
est une ArrayList dont le paramètre type est connu sous le nom Object
ou l'un de ses sous-types. (Oui, étend dans le type borns a une signification autre que direct subclass). Étant donné que seuls les types de référence peuvent être des paramètres de type, cela équivaut en fait à ArrayList<?>
.
Autrement dit, vous pouvez mettre un ArrayList<String>
dans une variable déclarée avec ArrayList<?>
. C'est pourquoi a1.add(3)
est une erreur de compilation. Le type déclaré de a1
autorise a1
à être un ArrayList<String>
, auquel aucun Integer
ne peut être ajouté.
De toute évidence, un ArrayList<?>
n’est pas très utile, car vous ne pouvez y insérer que null. C'est peut-être pourquoi la spécification Java interdit il:
C'est une erreur de compilation si l'un des les arguments de type utilisés dans une classe les expressions d'expression de création sont arguments de type joker
ArrayList<ArrayList<?>>
en revanche est un type de données fonctionnel. Vous pouvez y ajouter toutes sortes de ArrayLists et les récupérer. Et puisque ArrayList<?>
uniquement contient mais n'est pas un type de caractère générique, la règle ci-dessus ne s'applique pas.
Cela tient en grande partie au polymorphisme. Quand vous attribuez
X = new Y();
X peut être beaucoup moins «spécifique» que Y, mais pas l'inverse. X est juste la poignée avec laquelle vous accédez à Y, Y est la vraie chose instanciée,
Vous obtenez une erreur ici car Integer est un nombre, mais Number n'est pas un entier.
ArrayList<Integer> a = new ArrayList<Number>(); // compile-time error
En tant que telle, toute méthode de X que vous appelez doit être valide pour Y. Comme X est plus général, il partage probablement certaines méthodes de Y, mais pas toutes. Néanmoins, tous les arguments donnés doivent être valables pour Y.
Dans vos exemples avec add, un int (small i) n'est pas un objet ou un entier valide.
ArrayList<?> a = new ArrayList<?>();
Ce n'est pas bon, car vous ne pouvez pas réellement instancier une liste contenant des? Vous pouvez en déclarer un en tant que tel, puis vous pouvez presque tout suivre dans new ArrayList<Whatever>();
Pensez à ?
comme signifiant "unknown"
. Ainsi, "ArrayList<? extends Object>"
signifie "un type inconnu qui (ou tant qu'il s'étend) étend Object". Par conséquent, il est nécessaire de dire que arrayList.add(3)
mettrait quelque chose que vous savez dans un inconnu. I. 'Oublier'.