web-dev-qa-db-fra.com

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

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

J'avais l'habitude d'utiliser List<? extends T>, mais cela ne me permet pas d'ajouter des éléments à list.add(e), contrairement à List<? super T>.

658
Anand

extends

La déclaration générique de List<? extends Number> foo3 signifie que l’une quelconque de ces assignations 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. Reading - Compte tenu des assignations possibles ci-dessus, quel type d'objet vous est-il garanti de lire à partir de List foo3:

    • Vous pouvez lire une Number car toutes les listes pouvant être attribuées à foo3 contiennent une Number ou une sous-classe de Number.
    • Vous ne pouvez pas lire une Integer car foo3 pourrait pointer sur un List<Double>.
    • Vous ne pouvez pas lire une Double car foo3 pourrait pointer sur un List<Integer>.
  2. Writing - Compte tenu des assignations possibles ci-dessus, quel type d'objet pourriez-vous ajouter à List foo3 qui serait légal pour all les assignations ArrayList ci-dessus possibles:

    • Vous ne pouvez pas ajouter une Integer car foo3 pourrait pointer sur un List<Double>.
    • Vous ne pouvez pas ajouter Double car foo3 pourrait pointer sur List<Integer>.
    • Vous ne pouvez pas ajouter Number car foo3 pourrait pointer sur List<Integer>.

Vous ne pouvez ajouter aucun objet à List<? extends T> car vous ne pouvez pas garantir le type de List vers lequel elle pointe en réalité, vous ne pouvez donc pas garantir que l'objet est autorisé dans cette List. La seule "garantie" est celle vous pouvez seulement en lire et vous obtenez une T ou une sous-classe de T.

super

Considérons maintenant List <? super T>.

La déclaration générique de List<? super Integer> foo3 signifie que l’une quelconque de ces assignations 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. Reading - Compte tenu des assignations possibles ci-dessus, quel type d'objet vous est garanti de recevoir lorsque vous lisez à partir de List foo3:

    • Vous n'êtes pas assuré d'avoir une Integer car foo3 pourrait pointer vers un List<Number> ou un List<Object>.
    • Vous n'êtes pas assuré d'une Number car foo3 pourrait pointer vers un List<Object>.
    • La garantie seulement est que vous obtiendrez une instance de Object ou une sous-classe de Object (mais vous ne savez pas quelle sous-classe).
  2. Writing - Compte tenu des assignations possibles ci-dessus, quel type d'objet pourriez-vous ajouter à List foo3 qui serait légal pour all les assignations ArrayList ci-dessus possibles:

    • Vous pouvez ajouter une Integer car une Integer est autorisée 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ée dans l'une des listes ci-dessus.
    • Vous ne pouvez pas ajouter une Double car foo3 pourrait pointer sur un ArrayList<Integer>.
    • Vous ne pouvez pas ajouter une Number car foo3 pourrait pointer sur un ArrayList<Integer>.
    • Vous ne pouvez pas ajouter une Object car foo3 pourrait pointer sur un ArrayList<Integer>.

PECS

Rappelez-vous PECS: "Producer Extends, Consumer Super".

  • "Producer Extends" - Si vous avez besoin d'une List pour produire des valeurs T (vous voulez lire Ts de la liste), vous devez le déclarer avec ? extends T, par exemple. List<? extends Integer>. Mais vous ne pouvez pas ajouter à cette liste.

  • "Consumer Super" - Si vous avez besoin d'une List pour consommer des valeurs T (vous voulez écrire Ts dans la liste), vous devez le déclarer avec ? super T, par exemple. List<? super Integer>. Mais il n'y a aucune garantie sur le type d'objet que vous pouvez lire dans cette liste.

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

Exemple

Remarque Cet exemple tiré de Java Generics FAQ . Notez comment la liste source src (la liste productrice) utilise extends et la liste de destination dest (la liste consommatrice) 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 puis-je ajouter à la liste <? étend Nombre> structures de données?

1235
Bert F

Imaginez avoir cette hiérarchie

 enter image description here

1. étend

En écrivant 

    List<? extends C2> list;

vous dites que list pourra référencer 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 pouvant stocker C2 ou des sous-types) ou
  2. D1: __ new ArrayList<D1>(); (un objet pouvant stocker D1 ou des sous-types) ou
  3. D2: __ new ArrayList<D2>();, (un objet pouvant stocker D2 ou des sous-types) ou ...

etc. 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 (en rouge) représentés ici graphiquement

 enter image description here

Comme vous pouvez le constater, il n’existe pas de type sûr commun à tous les cas:

  • vous ne pouvez pas list.add(new C2(){}); car il pourrait s'agir de list = new ArrayList<D1>();
  • vous ne pouvez pas list.add(new D1(){}); car il pourrait s'agir de list = new ArrayList<D2>();

etc. 

2. Super

En écrivant 

    List<? super C2> list;

vous dites que list pourra référencer 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 pouvant stocker A1 ou des sous-types) ou
  • A2:new ArrayList<A2>();, (un objet pouvant stocker A2 ou des sous-types) ou
  • A3:new ArrayList<A3>();, (un objet pouvant stocker des A3 ou des sous-types) ou ...

etc. 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 (en rouge) représentés ici graphiquement

 enter image description here

Comme vous pouvez le constater, nous avons ici sept types sûrs communs à chaque cas: C2, D1, D2, E1, E2, E3, E4.

  • vous pouvez list.add(new C2(){}); car, quel que soit le type de liste que nous référons, C2 est autorisé
  • vous pouvez list.add(new D1(){}); car, quel que soit le type de liste que nous référons, D1 est autorisé

etc. Vous avez probablement remarqué que ces types correspondent à la hiérarchie à partir du type C2.

Remarques

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

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{}
191
Luigi Cortese

J'aime la réponse de @Bert F, mais c'est ainsi que mon cerveau le voit.

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

List<? super   X>

Si je reçois une liste et que je veux lire un X sur cette liste, il vaut mieux être une liste de X ou une liste de choses pouvant être retransmises en X lorsque je les lis, c'est-à-dire tout ce qui étend X

List<? extends X>

J'espère que cela t'aides. 

67
Michael Dausmann

Basé sur 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, essayons maintenant d’obtenir de la valeur de fruitExtendedList

Fruit fruit = fruitExtendedList.get(position)

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

Encore une fois permet d'essayer 

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.

Même est le cas pour 

WaterMelon waterMelon = fruitExtendedList.get(position)

Essayons maintenant de définir un objet dans fruitExtendedList

Ajout d'objet de 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'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 un 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 que se passe-t-il si quelqu'un décide de créer un nouveau type de citron? Disons, par arguments, que SaltyLemon est comme

public class SaltyLemon extends Lemon{}

Now fruitExtendedList peut être une liste de Fruit, Melon, WaterMelon ou SaltyLemon.

Donc, notre déclaration 

fruitExtendedList.add(new WaterMelon())

n'est pas valide non plus.

En gros, nous pouvons dire que nous ne pouvons rien écrire dans un fruitExtendedList.

Ceci 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 une 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 peut pas être lu.

Mais notons que nous pouvons lire des 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'objet de type d'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'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 les objets de type Object.

17
Sushant

super est une limite inférieure et étend est une limite supérieure.

Selon http://download.Oracle.com/javase/tutorial/extra/generics/morefun.html :

La solution consiste à utiliser une forme de joker que nous n’avons pas encore vu: joker avec une limite inférieure. Le la syntaxe? super T dénote une inconnue type qui est un supertype de T (ou T lui-même; rappelez-vous que la relation supertype est réflexive) C'est le double des caractères génériques liés, nous avons été en utilisant, où utilisons-nous? s'étend de T à désigne un type inconnu qui est un sous-type de T.

14
Istao

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

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

List<? extends T> - lecture et assignation:

|-------------------------|-------------------|---------------------------------|
|         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> - écriture et assignation:

|-------------------------|-------------------|-------------------------------------------|
|         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 dans une liste, quel que soit le caractère générique.
  • vous pouvez toujours ajouter null à une liste modifiable, quel que soit le caractère générique.
8
Oleksandr

L'utilisation de extend vous ne pouvez obtenir que de la collection. Vous ne pouvez pas y mettre. Aussi, bien que super permette à la fois d’obtenir et de mettre, le type de retour pendant l’obtention est? super T .

3
Sai Sunder

La chose la plus déroutante ici est que peu importe les restrictions de types que nous spécifions, l’attribution ne fonctionne que dans un sens:

baseClassInstance = derivedClassInstance;

Vous pouvez penser que Integer extends Number et qu'une Integer feraient un <? extends Number>, mais le compilateur vous dira que <? extends Number> cannot be converted to Integer (c'est-à-dire, en langage humain, il est faux que tout ce qui étend le nombre puisse être converti en Integer ):

class Holder<T> {
    T v;
    T get() { return v; }
    void set(T n) { v=n; }
}
class A {
    public static void main(String[]args) {
        Holder<? extends Number> he = new Holder();
        Holder<? super Number> hs = new Holder();

        Integer i;
        Number n;
        Object o;

        // Producer Super: always gives an error except
        //       when consumer expects just Object
        i = hs.get(); // <? super Number> cannot be converted to Integer
        n = hs.get(); // <? super Number> cannot be converted to Number
                      // <? super Number> cannot be converted to ... (but
                      //       there is no class between Number and Object)
        o = hs.get();

        // Consumer Super
        hs.set(i);
        hs.set(n);
        hs.set(o); // Object cannot be converted to <? super Number>

        // Producer Extends
        i = he.get(); // <? extends Number> cannot be converted to Integer
        n = he.get();
        o = he.get();

        // Consumer Extends: always gives an error
        he.set(i); // Integer cannot be converted to <? extends Number>
        he.set(n); // Number cannot be converted to <? extends Number>
        he.set(o); // Object cannot be converted to <? extends Number>
    }
}

hs.set(i); est ok parce queInteger PEUT &ECIRC;TRE CONVERTI EN N'IMPORTE QUELLE SUPERCLASSE DE Number(et non parce que Integer est une superclasse de Number, ce qui n'est pas vrai).

EDIT a ajouté un commentaire à propos de Consumer Extends et Producer Super - ils ne sont pas significatifs car ils ne spécifient pas en conséquence rien et justeObject. Il est conseillé de ne pas oublier PECS, car CEPS n’est jamais utile.

2

Quand utiliser les extensions et super

Les caractères génériques sont les plus utiles dans les paramètres de méthode. Ils permettent la flexibilité nécessaire dans les interfaces de méthodes.

Les gens sont souvent désorientés quant à l’utilisation des extensions et des super limites. La règle de base est le principe de départ. Si vous obtenez quelque chose d'un conteneur paramétré, utilisez extend.

int totalFuel(List<? extends Vehicle> list) {
int total = 0;
for(Vehicle v : list) {
    total += v.getFuel();
}
return total;}

La méthode totalFuel extrait les véhicules de la liste, leur demande de quelle quantité de carburant ils disposent et calcule le total . Si vous placez des objets dans un conteneur paramétré, utilisez super.

int totalValue(Valuer<? super Vehicle> valuer) {
int total = 0;
for(Vehicle v : vehicles) {
    total += valuer.evaluate(v);
}
return total;}

La méthode totalValue place les véhicules dans l’évaluateur . Il est utile de savoir que l’étendue des liens est beaucoup plus courante que super.

1
Kevin STS

Les réponses votées à la hausse couvrent les détails sur de nombreux aspects. Cependant, j'essaierais de répondre à cela différemment.

Il y a 2 choses que nous devons considérer, 

1. Affectation à la variable de liste

List<? extends X> listvar;

Ici, on peut attribuer n'importe quelle liste de X ou liste de sous-classes de X} à listvar.

List<? extends Number> listvar; listvar = new ArrayList<Number>(); listvar = new ArrayList<Integer>();


List<? super X> listvar;

Ici, toute liste de X ou liste de superclasses de X peut être affectée à listvar.

List<? super Number> listvar; listvar = new ArrayList<Number>(); listvar = new ArrayList<Object>();

2. Effectuer une opération de lecture ou d’écriture sur la variable de liste

`List<? extends X> listvar;`

Vous pouvez utiliser cette fonctionnalité pour accepter une liste dans les arguments de méthode et effectuer toute opération sur type X (Remarque: vous pouvez uniquement lire les objets de type X de la liste).

`List<? super Number> listvar;

Vous pouvez utiliser cette fonctionnalité pour accepter une liste dans les arguments de méthode et effectuer toute opération sur type Object as as lecture seule des objets de type Object de la liste. Mais oui, vous pouvez ajouter des objets de type X à la liste.

1
Shailesh Pratapwar

Liste <? étend X> ne permet pas d’ajouter quoi que ce soit, sauf null, dans la liste.

Liste <? super X> permet d’ajouter tout ce qui est un X (X ou sa sous-classe) ou null.

1
elyor

Vous pouvez parcourir toutes les réponses ci-dessus pour comprendre pourquoi la .add() est limitée à '<?>', '<? extends>' et partiellement à '<? super>'.

Mais voici la conclusion de tout cela si vous voulez vous en souvenir, et ne voulez pas aller explorer la réponse à chaque fois:

List<? extends A> signifie que cela acceptera toute List de A et sous-classe de A. Mais vous ne pouvez rien ajouter à cette liste. Pas même les objets de type A.

List<? super A> signifie que cela acceptera n'importe quelle liste de A et la superclasse de A. Vous pouvez ajouter des objets de type A et ses sous-classes.

1
jforex78

Les caractères génériques génériques ciblent deux besoins principaux:

Lecture à partir d'une collection générique Insertion dans une collection générique Il existe trois façons de définir une collection (variable) à l'aide de caractères génériques génériques. Ceux-ci sont:

List<?>           listUknown = new ArrayList<A>();
List<? extends A> listUknown = new ArrayList<A>();
List<? super   A> listUknown = new ArrayList<A>();

List<?> signifie une liste tapée à un type inconnu. Cela pourrait être un List<A>, un List<B>, un List<String> etc.

List<? extends A> signifie une liste d'objets qui sont des instances du class A ou subclasses of A (par exemple, B et C) .List<? super A> signifie que la liste est tapée dans le A class ou un superclass of A.

En savoir plus: http://tutorials.jenkov.com/Java-generics/wildcards.html

1
Vaibhav Gupta
                                       |
                                       v

Liste <? étend X> ? peut être n'importe quelle sous-classe de X ou elle-même X.

Liste <? super X> ? peut être n'importe quelle superclasse de X ou elle-même X.

                                      ^
                                      |
0
snr

Exemple, L'ordre d'héritage est supposé de O> S> T> U> V

Utilisation de extend keyword,

Correct:

List<? extends T> Object = new List<T>();
List<? extends T> Object = new List<U>();
List<? extends T> Object = new List<V>();

Incorrect:

List<? extends T> Object = new List<S>();
List<? extends T> Object = new List<O>();

super Mot clé:  

Correct:

List<? super T> Object = new List<T>();
List<? super T> Object = new List<S>();
List<? super T> Object = new List<O>();

Incorrect:

List<? super T> Object = new List<U>();
List<? super T> Object = new List<V>();

Ajout d'un objet: List Object = new List ();

Object.add(new T()); //error

Mais pourquoi erreur? Examinons les possibilités d’initialisation d’un objet de liste

List<? extends T> Object = new List<T>();
List<? extends T> Object = new List<U>();
List<? extends T> Object = new List<V>();

Si nous utilisons Object.add (new T ()); alors il ne sera correct que si 

List<? extends T> Object = new List<T>(); 

Mais il y a deux possibilités supplémentaires

Objet List = new List (); Objet List = new List (); Si nous essayons d’ajouter (new T()) aux deux possibilités ci-dessus, cela donnera une erreur car T est la classe supérieure de U et V. nous essayons d'ajouter un objet T [qui est (new T())] à la liste de types U et V. Les objets de classe supérieure (classe de base) ne peuvent pas être transmis à la classe inférieure Object (sous-classe).

En raison des deux possibilités supplémentaires, Java vous donne une erreur même si vous utilisez la bonne solution, car Java ne sait pas à quel objet vous vous référez. Vous ne pouvez donc pas ajouter d'objets à List Object = new List (); car il y a des possibilités qui ne sont pas valides.

Ajout d'un objet: List Object = new List ();

Object.add(new T()); // compiles fine without error
Object.add(new U()); // compiles fine without error
Object.add(new V()); // compiles fine without error

Object.add(new S()); //  error
Object.add(new O()); //  error

Mais pourquoi une erreur se produit dans les deux précédents? Nous pouvons utiliser Object.add (new T ()); seulement sur les possibilités ci-dessous,

List<? super T> Object = new List<T>();
List<? super T> Object = new List<S>();
List<? super T> Object = new List<O>();

Si nous avons essayé d'utiliser Object.add (new T()) dans Objet de liste = new List (); et Objet de liste = new List (); alors cela donnera une erreur C'est parce que Nous ne pouvons pas ajouter d'objet T [qui est new T ()] à l'objet List = new List (); parce que c'est un objet de type U. Nous ne pouvons pas ajouter d'objet T [qui est nouveau T ()] à U Object car T est une classe de base et U est une sous-classe. Nous ne pouvons pas ajouter de classe de base à la sous-classe et c'est pourquoi une erreur se produit. C'est pareil pour l'autre cas.

0
Crox