[Java] Présentation du type générique de limite générique

Rôle de type générique de limite

Les types génériques Java ne peuvent pas convertir les types «List » en 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.

Fruitクラス.png

Type de caractère générique avec limite supérieure

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

?_extends_fruit.png

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.

Lis

Conversion de «Type générique à limite limitée» en «Type de classe T»

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

Conversion du "type générique de limite supérieure" en "type de sous-classe de 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.

  1. ArrayList<Fruit>
  2. ArrayList<Melon>
  3. 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.

l'écriture

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.

Type de caractère générique avec limite inférieure

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

?_super_melon.png

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.

l'écriture

Conversion en type générique de limite inférieure

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

Lis

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)

Résumé

Appendix

Qu'est-ce que cela signifie d'écrire une valeur avec un type générique de limite

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

[Java] Présentation du type générique de limite générique
[Java] Génériques
Résumé des génériques Java
Présentation de la bibliothèque
[Java] Exemple de génériques
Le type d'intersection introduit dans Java 10 est incroyable (?)
Génériques Java (Notes)
[Java] Type d'énumération
Java Type facultatif
[Java] Faites attention au type de clé de la carte
Java double type
À propos de la signification des variables de type, E, T, etc. utilisées dans les génériques utilisés en Java
Mémo organisé dans la tête (Java - type de données)
Java getClass () est incompatible avec les génériques (ou les variables de type)
[Java] Type de données ①-Type de base
Génériques Java fréquemment utilisés
null spécifie le type
[Java, Kotlin] Variance de type
Champ de type de classe Java
Détermination de type en Java
Étudier Java # 1 (type typique)
[Java] Conversion de type de date
Trouvez la classe d'adresse et le type d'adresse à partir de l'adresse IP avec Java
[Java] Pour connaître les informations de type des paramètres de type à l'exécution