Les types génériques Java ne peuvent pas convertir les types «List
Différents types
//Si les paramètres de type OK sont les mêmes
List<Integer> foo = new ArrayList<Integer>();
// NG List<Integer>Est une liste<Number>Ne peut pas être converti en
List<Number> bar = foo;
//De même, la conversion n'est pas possible même avec l'initialisation NG
List<Number> foo = new ArrayList<Integer>();
Par conséquent, en utilisant ** le type générique de limite **, ** les paramètres de type peuvent être déclarés avec une certaine largeur **. Cependant, il existe deux types de types de caractères génériques de limite. En effet, lorsque les limites de type sont définies, le problème que le système de traitement ne peut pas définir le type se produit dans chaque situation. Regardons le problème un par un.
La classe Fruit suivante est utilisée pour l'explication.
Generic<? extends T>
Si vous spécifiez un paramètre de type comme ci-dessus, le paramètre de type est ** type générique limité **. Le type ? Extend T </ kbd> signifie ** T ou un type qui représente toutes ses sous-classes **.
En considérant l'utilisation du type List <? Extends Fruit>
, il est possible d'effectuer une conversion à partir de la ** classe Fruit ou du type List ** qui spécifie ses sous-classes.
Liste des convertibles Liste
List<? extends Fruit> basket = new ArrayList<Fruit>(); // 1
List<? extends Fruit> basket = new ArrayList<Melon>(); // 2
List<? extends Fruit> basket = new ArrayList<Lemon>(); // 3
Ensuite, envisagez de récupérer des éléments de la variable Liste <? Extends Fruit> panier
,
Veuillez noter que la variable de panier peut être récupérée en tant que classe Fruit, qu'elle soit 1, 2 ou 3.
Pensez à récupérer des éléments de la variable Liste <? Extends Fruit> panier
.
Dans le cas du type générique avec une limite supérieure, ** au moins le type qui représente la classe T et ses sous-classes **, il est donc possible de spécifier la limite supérieure T lors de la récupération d'un élément de List.
Convertir le type générique plafonné en type T
List<? extends Fruit> fruits = ...
//aucun problème
Fruit fruit = fruits.get(i);
Il s'agit d'une conversion du type «? Etend T» en type «T».
Ensuite, lorsque vous récupérez un élément de la variable panier, déterminez si sa sous-classe peut être spécifiée.
Le type List <? Extends Fruit>
peut être converti à partir d'au moins les trois types de liste suivants.
ArrayList<Fruit>
ArrayList<Melon>
ArrayList<Lemon>
Par conséquent, lors de la récupération d'un élément, il n'est pas possible de spécifier une sous-classe qui dérive de plusieurs séries.
Le type générique plafonné ne peut pas être converti en type de sous-classe
List<? extends Fruit> basket = ...
//Erreur de compilation NG Impossible de convertir en type Melon
Melon fruit = basket.get(i);
Ci-dessus, si le type «? Extends Fruit» peut être converti en sous-classe, les incohérences suivantes se produisent.
Convertir le type générique plafonné en T sous-classe de type 1
//Faites une liste de melons
List<Melon> melonList = new ArrayList<Melon>()
melonList.add(new Melon());
// List<Melon>Type de liste melonList<? extends Fruit>Convertir en type
List<? extends Fruit> basket = melonList;
//S'il peut être converti en sous-classe, il sera possible de le recevoir en tant que citron.
Lemon fruit = basket.get(i);
Par conséquent, la conversion en sous-classes n'est pas autorisée. Par conséquent, la conversion du type «? Etend T» en type «sous-classe» de »T n'est pas possible.
Ensuite, pensez à ajouter un élément à la variable List <? Extends Fruit> fruits
.
Puisque vous pouvez convertir du type «? Étend Fruit» en «Type de fruit», il semble que vous puissiez ajouter un objet Fruit à la variable fruits suivante.
Écrire dans le type générique plafonné
List<? extends Fruit> fruits = new ArrayList<Fruit>();
//Cela semble possible à première vue
fruits.add(new Fruit());
Cependant, si ce qui précède est autorisé, les incohérences suivantes se produiront.
L'écriture dans le type générique plafonné n'est pas cohérente
//La réalité des fruits est une liste de melons
List<? extends Fruit> fruits = new ArrayList<Melon>();
//La classe Lemon est une sous-classe de la classe Fruit
Fruit fruit = new Lemon();
//Le citron sera ajouté à la liste des melons
fruits.add(fruit);
Par conséquent, il n'est pas possible de convertir le type concret de type «T» ou de sous-classe «T» en type «Étend T». Cela signifie que si vous déclarez un type List avec un caractère générique plafonné, vous ne pouvez pas ajouter d'éléments. C'est là qu'intervient un autre type de joker de bordure.
Generic<? super T>
Si vous spécifiez un paramètre de type comme ci-dessus, il s'agit ** d'un type générique avec une limite inférieure **. Le type ? Super T </ kbd> signifie ** T ou toutes ses superclasses **.
Vous pouvez convertir en type ** List \ <? Super Melon > ** à partir de la ** classe Fruit ou du type List ** dont le paramètre de type est la classe Fruit ou sa super classe.
Liste des convertibles Liste
List<? super Melon> baskets = new ArrayList<Melon>(); // 1
List<? super Melon> baskets = new ArrayList<Fruit>(); // 2
List<? super Melon> baskets = new ArrayList<Food>(); // 3
List<? super Melon> baskets = new ArrayList<Object>(); // 4
Cette fois-ci, je penserai d'abord à ajouter des éléments à la variable panier, Veuillez noter que la classe Melon peut être ajoutée à la variable panier pour 1 à 4, 1 à 2, 3 et 4.
Pour ajouter un élément à une variable List <? Super Melon> basket
, dans le cas d'un type wildcard avec une limite inférieure, c'est ** un type qui représente au moins une classe T ou une super classe supérieure **, donc un élément d'un type List Lors de l'ajout, vous devriez pouvoir spécifier sa limite inférieure, T.
Vérifiez que vous pouvez ajouter un par un en vous référant à l'exemple.
Quand List <? Super Melon> fruits = new ArrayList <Object> ()
Ajouter à la liste d'objets
// OK
fruits.add(new Melon())
// OK
fruits.add(new WaterMelon());
Quand List <? Super Melon> fruits = new ArrayList <Food> ()
Ajouter à la liste d'aliments
// OK
fruits.add(new Melon())
// OK
fruits.add(new WaterMelon());
Quand List <? Super Melon> fruits = new ArrayList <Melon> ()
Ajouter à la liste Melon
// OK
fruits.add(new Melon());
// OK
fruits.add(new WaterMelon());
En d'autres termes, il est possible de convertir un type «T» ou «T sous-classe» en un type «Super T».
Le type ? Super Melon </ kbd> est un "type qui représente la classe Melon et toutes ses super classes". De plus, toutes les classes Java héritent du type ** Object **. Par conséquent, la conversion du type «? Super T» en type «Objet» est possible.
Conversion du type générique avec limite inférieure au type d'objet
Object object = basket.get(i)
Les types de paramètres de limite peuvent être utilisés pour représenter un large éventail de types génériques.
Table de conversion de type joker et de type de béton avec limite supérieure
Oui Non | Source de conversion | Destination de conversion | Sa signification |
---|---|---|---|
OK | ? extends T |
T |
T Peut obtenir une valeur de type |
NG | T |
? extends T |
Ne peut pas écrire |
Table de conversion de type joker et de type de béton avec limite inférieure
Oui Non | Source de conversion | Destination de conversion | Sa signification |
---|---|---|---|
OK | ? super T |
Object |
La valeur peut être obtenue comme type d'objet |
OK | T |
? super T |
La valeur de type T peut être écrite |
OK | Sous-classe T |
? super T |
Valeur inscriptible du type de sous-classe de T |
Généralement, il existe un sort appelé ** PECS **, et le type générique borné avec limite supérieure est parfois appelé Producteur, et le type générique borné avec limite inférieure est parfois appelé Consommateur.
Producteur spécialisé dans la création de valeur
Consommateur spécialisé dans la réception de valeurs
Appendix
Vous avez peut-être entendu dire que lorsque vous spécifiez un type générique borné avec une limite supérieure, la liste ne peut pas être écrite.
Cela signifie simplement que vous ne pouvez pas:
** Impossible de passer du "type concret" au "type générique plafonné" **
Si vous déclarez un objet de type générique avec un caractère générique de limite supérieure, la conversion ci-dessus se produira lorsque vous appelez une fonction avec ce paramètre de type.
Considérez la classe Stack \ <T > ci-dessous.
Classe de pile
class Stack<T> {
void push(T e) {}
}
Déclarez la classe Stack \ <T > avec un type générique plafonné.
Stack<? extends Fruit> basket = new Stack<Fruit>();
A ce moment, le paramètre de type T de la variable panier est de type «? Extends Fruit». La méthode push équivaut donc à:
Lorsque le paramètre de type de pile est un caractère générique plafonné
void push(`? extends Fruit` e) {}
Par conséquent, l'appel de cette méthode push entraîne une conversion d'un type «Fruit» en un type «Extends Fruit». Et cette conversion est NG.
//NG Type de fruit`? extends Fruit`Ne peut pas être converti en type
basket.push(new Fruit());
Recommended Posts