web-dev-qa-db-fra.com

Génériques Java - Initialisation ArrayList

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 
20
user467871

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<?>.

13
ColinD

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 As. Plus explicitement, a est une référence à une liste de tableaux qui acceptera As et produira As. new ArrayList<A>() est une instance d'une liste de tableaux de As, c'est-à-dire une liste de tableaux qui acceptera As et produira As.

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 Integers et produisant Integers. Il ne peut pas pointer sur une liste de tableaux de Numbers. Cette liste de Numbers ne peut pas respecter toutes les promesses de ArrayList<Integer> a (c’est-à-dire qu’une liste de Numbers peut produire des objets qui ne sont pas Integers, 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 Numbers, c’est-à-dire à une liste de tableaux qui acceptera Numbers et produira Numbers. Il ne peut pas pointer sur une liste de tableaux de Integers, car la déclaration de type de a indique que a peut accepter n'importe quel Number, mais que cette liste de Integers ne peut accepter aucun Number, mais uniquement Integers.

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 produireObjects. 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 Integers ne peut accepter que des Integers.)

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 Integers, mais elle ne peut pas produire Integers. 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 Integers.

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?

8
Bert F

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.

2
meriton

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>();

1
jon_darkstar

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'.

0
Dudus