1056 votes

Différence entre < ? super T> et < ? extends T> en Java

Quelle est la différence entre List<? super T> y List<? extends T> ?

J'avais l'habitude d'utiliser List<? extends T> mais il ne me permet pas d'y ajouter des éléments. list.add(e) alors que le List<? super T> fait.

41 votes

Consultez cet excellent exposé : youtu.be/V1vQf4qyMXg?t=22m24s

1 votes

Ici null nous pouvons ajouter

0 votes

Une très bonne explication avec un exemple @ youtube.com/watch?v=34oiEq9nD0M&feature=youtu.be&t=1630 qui explique la partie < ? super T> mais, donne une idée d'une autre.

2073voto

Bert F Points 27237

extends

La déclaration joker de List<? extends Number> foo3 signifie que toutes ces missions sont légales :

List<? extends Number> foo3 = new ArrayList<Number>();  // Number "extends" Number (in this context)
List<? extends Number> foo3 = new ArrayList<Integer>(); // Integer extends Number
List<? extends Number> foo3 = new ArrayList<Double>();  // Double extends Number
  1. Lecture - Compte tenu des affectations possibles ci-dessus, quel type d'objet êtes-vous sûr de lire à partir de List foo3 :

    • Vous pouvez lire un Number parce que n'importe laquelle des listes qui pourraient être assignées à foo3 contiennent un Number ou une sous-classe de Number .
    • Vous ne pouvez pas lire un Integer parce que foo3 pourrait pointer vers un List<Double> .
    • Vous ne pouvez pas lire un Double parce que foo3 pourrait pointer vers un List<Integer> .
  2. Rédaction - Compte tenu des affectations possibles ci-dessus, quel type d'objet pouvez-vous ajouter à List foo3 qui serait légal pour todo les possibilités ci-dessus ArrayList les assignations :

    • Vous ne pouvez pas ajouter un Integer parce que foo3 pourrait pointer vers un List<Double> .
    • Vous ne pouvez pas ajouter un Double parce que foo3 pourrait pointer vers un List<Integer> .
    • Vous ne pouvez pas ajouter un Number parce que foo3 pourrait pointer vers un List<Integer> .

Vous ne pouvez pas ajouter d'objet à List<? extends T> parce que vous ne pouvez pas garantir quel genre de List vers lequel il pointe réellement, donc vous ne pouvez pas garantir que l'objet est autorisé dans cette zone. List . La seule "garantie" est que vous ne pouvez que le lire et que vous obtiendrez une T ou sous-classe de T .

super

Considérons maintenant List <? super T> .

La déclaration joker de List<? super Integer> foo3 signifie que toutes ces missions sont légales :

List<? super Integer> foo3 = new ArrayList<Integer>();  // Integer is a "superclass" of Integer (in this context)
List<? super Integer> foo3 = new ArrayList<Number>();   // Number is a superclass of Integer
List<? super Integer> foo3 = new ArrayList<Object>();   // Object is a superclass of Integer
  1. Lecture - Compte tenu des affectations possibles ci-dessus, quel type d'objet êtes-vous sûr de recevoir lorsque vous lisez le fichier List foo3 :

    • On ne vous garantit pas un Integer parce que foo3 pourrait pointer vers un List<Number> o List<Object> .
    • Vous n'avez pas la garantie d'un Number parce que foo3 pourrait pointer vers un List<Object> .
    • El uniquement La garantie est que vous obtiendrez une instance d'une Object ou sous-classe de Object (mais vous ne savez pas quelle sous-classe).
  2. Rédaction - Compte tenu des affectations possibles ci-dessus, quel type d'objet pouvez-vous ajouter à List foo3 qui serait légal pour todo les possibilités ci-dessus ArrayList les assignations :

    • Vous pouvez ajouter un Integer car un Integer est autorisé dans l'une des listes ci-dessus.
    • Vous pouvez ajouter une instance d'une sous-classe de Integer car une instance d'une sous-classe de Integer est autorisé dans l'une des listes ci-dessus.
    • Vous ne pouvez pas ajouter un Double parce que foo3 pourrait pointer vers un ArrayList<Integer> .
    • Vous ne pouvez pas ajouter un Number parce que foo3 pourrait pointer vers un ArrayList<Integer> .
    • Vous ne pouvez pas ajouter un Object parce que foo3 pourrait pointer vers un ArrayList<Integer> .

PECS

Souvenez-vous de PECS : "Le producteur prolonge, le consommateur super" .

  • "Le producteur prolonge" - Si vous avez besoin d'un List pour produire T valeurs (vous voulez lire T de la liste), vous devez le déclarer avec l'option ? extends T par exemple List<? extends Integer> . Mais vous ne pouvez pas ajouter à cette liste.

  • "Super consommateur" - Si vous avez besoin d'un List pour consommer T (vous voulez écrire T dans la liste), vous devez le déclarer avec la commande ? super T par exemple List<? super Integer> . Mais il n'y a aucune garantie quant au type d'objet que vous pourrez lire dans cette liste.

  • Si vous devez à la fois lire et écrire dans une liste, vous devez la déclarer exactement, sans caractères de remplacement, par exemple List<Integer> .

Exemple

Note cet exemple tiré de la FAQ Java Generics . Notez comment le répertoire des sources src (la liste de production) utilise extends et la liste des destinations dest (la liste de consommation) utilise super :

public class Collections { 
  public static <T> void copy(List<? super T> dest, List<? extends T> src) {
      for (int i = 0; i < src.size(); i++) 
        dest.set(i, src.get(i)); 
  } 
}

Voir aussi Comment ajouter aux structures de données List< ? extends Number> ?

1 votes

J'ai un doute ici : Si j'ai besoin d'ajouter des données à une liste générique et d'en extraire des données, que dois-je faire ?

2 votes

@Anand - Voir le troisième point sous PECS ci-dessus. Vous aurez probablement besoin d'utiliser un paramètre générique spécifique (sans caractère générique, par ex. List<T> ). Malheureusement, vous ne pouvez pas tout avoir. Vous pouvez soit créer une référence flexible à la liste en utilisant un caractère générique (par ex. <? super T> o <? extends T> et souffre de limitations quant à ce que vous pouvez y ajouter ou y lire. Ou bien vous pouvez utiliser une référence moins flexible à une liste d'un type spécifique (par ex. List<T> ) et obtenir de meilleures garanties quant à ce que vous pouvez lire/écrire à partir de celui-ci.

1 votes

Comme exemple, vous pouvez voir la méthode de copie des collections public static <T> void copy(List< ? super T> dest, List< ? extends T> src) { @ docjar.com/html/api/java/util/Collections.java.html

341voto

Luigi Cortese Points 1664

Imaginez que vous ayez cette hiérarchie

enter image description here

1. Prolonge le site

En écrivant

    List<? extends C2> list;

vous dites que list pourra faire référence à un objet de type (par exemple) ArrayList dont le type générique est l'un des 7 sous-types de C2 ( C2 inclus) :

  1. C2 : new ArrayList<C2>(); (un objet qui peut stocker des C2 ou des sous-types) ou
  2. D1 : new ArrayList<D1>(); (un objet qui peut stocker D1 ou des sous-types) ou
  3. D2 : new ArrayList<D2>(); , (un objet qui peut stocker des D2 ou des sous-types) ou...

et ainsi de suite. Sept cas différents :

    1) new ArrayList<C2>(): can store C2 D1 D2 E1 E2 E3 E4
    2) new ArrayList<D1>(): can store    D1    E1 E2  
    3) new ArrayList<D2>(): can store       D2       E3 E4
    4) new ArrayList<E1>(): can store          E1             
    5) new ArrayList<E2>(): can store             E2             
    6) new ArrayList<E3>(): can store                E3             
    7) new ArrayList<E4>(): can store                   E4             

Nous avons un ensemble de types "stockables" pour chaque cas possible : 7 ensembles (rouges) ici représentés graphiquement

enter image description here

Comme vous pouvez le voir, il n'y a pas de type de sécurité qui est commune à tous les cas :

  • vous ne pouvez pas list.add(new C2(){}); parce que ça pourrait être list = new ArrayList<D1>();
  • vous ne pouvez pas list.add(new D1(){}); parce que ça pourrait être list = new ArrayList<D2>();

et ainsi de suite.

2. Super

En écrivant

    List<? super C2> list;

vous dites que list pourra faire référence à un objet de type (par exemple) ArrayList dont le type générique est l'un des 7 supertypes de C2 ( C2 inclus) :

  • A1 : new ArrayList<A1>(); (un objet qui peut stocker des A1 ou des sous-types) ou
  • A2 : new ArrayList<A2>(); (un objet qui peut stocker des A2 ou des sous-types) ou
  • A3 : new ArrayList<A3>(); , (un objet qui peut stocker des A3 ou des sous-types) ou...

et ainsi de suite. Sept cas différents :

    1) new ArrayList<A1>(): can store A1          B1 B2       C1 C2    D1 D2 E1 E2 E3 E4
    2) new ArrayList<A2>(): can store    A2          B2       C1 C2    D1 D2 E1 E2 E3 E4
    3) new ArrayList<A3>(): can store       A3          B3       C2 C3 D1 D2 E1 E2 E3 E4
    4) new ArrayList<A4>(): can store          A4       B3 B4    C2 C3 D1 D2 E1 E2 E3 E4
    5) new ArrayList<B2>(): can store                B2       C1 C2    D1 D2 E1 E2 E3 E4
    6) new ArrayList<B3>(): can store                   B3       C2 C3 D1 D2 E1 E2 E3 E4
    7) new ArrayList<C2>(): can store                            C2    D1 D2 E1 E2 E3 E4

Nous avons un ensemble de types "stockables" pour chaque cas possible : 7 ensembles (rouges) ici représentés graphiquement

enter image description here

Comme vous pouvez le voir, nous avons ici sept types de coffre-fort qui sont communes à tous les cas : C2 , D1 , D2 , E1 , E2 , E3 , E4 .

  • vous pouvez list.add(new C2(){}); parce que, quel que soit le type de liste auquel on se réfère, C2 est autorisé
  • vous pouvez list.add(new D1(){}); parce que, quel que soit le type de liste auquel on se réfère, D1 est autorisé

et ainsi de suite. Vous avez probablement remarqué que ces types correspondent à la hiérarchie partant du type C2 .

Notas

Voici la hiérarchie complète si vous souhaitez faire des essais

interface A1{}
interface A2{}
interface A3{}
interface A4{}

interface B1 extends A1{}
interface B2 extends A1,A2{}
interface B3 extends A3,A4{}
interface B4 extends A4{}

interface C1 extends B2{}
interface C2 extends B2,B3{}
interface C3 extends B3{}

interface D1 extends C1,C2{}
interface D2 extends C2{}

interface E1 extends D1{}
interface E2 extends D1{}
interface E3 extends D2{}
interface E4 extends D2{}

163voto

Michael Dausmann Points 985

J'aime la réponse de @Bert F mais c'est la façon dont mon cerveau voit les choses.

J'ai un X dans ma main. Si je veux écrire mes X dans une liste, cette liste doit être soit une liste de X, soit une liste de choses vers lesquelles mes X peuvent être convertis au fur et à mesure que je les écris, c'est-à-dire n'importe quelle liste de X. superclasse de X...

List<? super   X>

Si je reçois une liste et que je veux lire un X de cette liste, il vaut mieux qu'il s'agisse d'une liste de X ou d'une liste de choses qui peuvent être transformées en X au fur et à mesure que je les lis. étend X

List<? extends X>

J'espère que cela vous aidera.

50voto

Oleksandr Points 7545

J'aimerais visualiser la différence. Supposons que nous ayons :

class A { }
class B extends A { }
class C extends B { }

List<? extends T> - lire et assigner :

|-------------------------|-------------------|---------------------------------|
|         wildcard        |        get        |              assign             |
|-------------------------|-------------------|---------------------------------|
|    List<? extends C>    |    A    B    C    |                       List<C>   |
|-------------------------|-------------------|---------------------------------|
|    List<? extends B>    |    A    B         |             List<B>   List<C>   |
|-------------------------|-------------------|---------------------------------|
|    List<? extends A>    |    A              |   List<A>   List<B>   List<C>   |
|-------------------------|-------------------|---------------------------------|

List<? super T> - la rédaction et l'affectation :

|-------------------------|-------------------|-------------------------------------------|
|         wildcard        |        add        |                   assign                  |
|-------------------------|-------------------|-------------------------------------------|
|     List<? super C>     |              C    |  List<Object>  List<A>  List<B>  List<C>  |
|-------------------------|-------------------|-------------------------------------------|
|     List<? super B>     |         B    C    |  List<Object>  List<A>  List<B>           |
|-------------------------|-------------------|-------------------------------------------|
|     List<? super A>     |    A    B    C    |  List<Object>  List<A>                    |
|-------------------------|-------------------|-------------------------------------------|

Dans tous les cas :

  • vous pouvez toujours obtenir Object à partir d'une liste, sans tenir compte du caractère générique.

  • vous pouvez toujours ajouter null à une liste mutable sans tenir compte du joker.

35voto

Sushant Points 1308

Sur la base de La réponse de Bert F Je voudrais expliquer ma compréhension.

Disons que nous avons 3 classes comme

public class Fruit{}

public class Melon extends Fruit{}

public class WaterMelon extends Melon{}

Ici, nous avons

List<? extends Fruit> fruitExtendedList = …

//Says that I can be a list of any object as long as this object extends Fruit.

Ok, maintenant essayons d'obtenir une valeur de fruitExtendedList.

Fruit fruit = fruitExtendedList.get(position)

//This is valid as it can only return Fruit or its subclass.

Essayons encore une fois

Melon melon = fruitExtendedList.get(position)

//This is not valid because fruitExtendedList can be a list of Fruit only, it may not be 
//list of Melon or WaterMelon and in java we cannot assign sub class object to 
//super class object reference without explicitly casting it.

C'est également le cas pour

WaterMelon waterMelon = fruitExtendedList.get(position)

Maintenant, essayons de définir un objet dans fruitExtendedList.

Ajout d'un objet fruit

fruitExtendedList.add(new Fruit())

//This in not valid because as we know fruitExtendedList can be a list of any 
//object as long as this object extends Fruit. So what if it was the list of  
//WaterMelon or Melon you cannot add Fruit to the list of WaterMelon or Melon.

Ajout d'un objet Melon

fruitExtendedList.add(new Melon())

//This would be valid if fruitExtendedList was the list of Fruit but it may 
//not be, as it can also be the list of WaterMelon object. So, we see an invalid 
//condition already.

Enfin, essayons d'ajouter l'objet WaterMelon

fruitExtendedList.add(new WaterMelon())

//Ok, we got it now we can finally write to fruitExtendedList as WaterMelon 
//can be added to the list of Fruit or Melon as any superclass reference can point 
//to its subclass object.

Mais attendez. Et si quelqu'un décidait de créer un nouveau type de citron, disons pour argumenter SaltyLemon comme

public class SaltyLemon extends Lemon{}

Maintenant, fruitExtendedList peut être une liste de fruits, de melons, de pastèques ou de citrons salés.

Ainsi, notre déclaration

fruitExtendedList.add(new WaterMelon())

n'est pas non plus valable.

En gros, on peut dire qu'on ne peut rien écrire dans une fruitExtendedList.

Cela résume List<? extends Fruit>

Voyons maintenant

List<? super Melon> melonSuperList= …

//Says that I can be a list of anything as long as its object has super class of Melon.

Essayons maintenant d'obtenir de la valeur de melonSuperList.

Fruit fruit = melonSuperList.get(position)

//This is not valid as melonSuperList can be a list of Object as in java all 
//the object extends from Object class. So, Object can be super class of Melon and 
//melonSuperList can be a list of Object type

De même, Melon, WaterMelon ou tout autre objet ne peuvent être lus.

Mais notez que nous pouvons lire les instances de type Object

Object myObject = melonSuperList.get(position)

//This is valid because Object cannot have any super class and above statement 
//can return only Fruit, Melon, WaterMelon or Object they all can be referenced by
//Object type reference.

Maintenant, essayons de définir une valeur de melonSuperList.

Ajout d'un objet de type objet

melonSuperList.add(new Object())

//This is not valid as melonSuperList can be a list of Fruit or Melon.
//Note that Melon itself can be considered as super class of Melon.

Ajout d'un objet de type Fruit

melonSuperList.add(new Fruit())

//This is also not valid as melonSuperList can be list of Melon

Ajout d'un objet de type Melon

melonSuperList.add(new Melon())

//This is valid because melonSuperList can be list of Object, Fruit or Melon and in 
//this entire list we can add Melon type object.

Ajout d'un objet de type WaterMelon

melonSuperList.add(new WaterMelon())

//This is also valid because of same reason as adding Melon

Pour résumer, nous pouvons ajouter Melon ou sa sous-classe dans melonSuperList et lire uniquement l'objet de type Object.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X