Qu'est-ce que PECS (Producer Extends Consumer Super)?


729

Je suis tombé sur PECS (abréviation de Producer extendsand Consumersuper ) en lisant sur les génériques.

Quelqu'un peut-il m'expliquer comment utiliser PECS pour résoudre la confusion entre extendset super?


3
Une très bonne explication avec un exemple @ youtube.com/watch?v=34oiEq9nD0M&feature=youtu.be&t=1630 qui explique une superpartie mais donne une idée d'une autre.
lupchiazoem

Réponses:


844

tl; dr: "PECS" est du point de vue de la collection. Si vous ne tirez que des éléments d'une collection générique, c'est un producteur et vous devez utiliser extends; si vous ne rembourrez que des articles, c'est un consommateur et vous devez l'utiliser super. Si vous faites les deux avec la même collection, vous ne devez pas utiliser ni extendsou super.


Supposons que vous ayez une méthode qui prend comme paramètre une collection de choses, mais que vous souhaitiez qu'elle soit plus flexible que d'accepter simplement a Collection<Thing>.

Cas 1: Vous voulez parcourir la collection et faire des choses avec chaque article.
Ensuite, la liste est un producteur , vous devez donc utiliser un Collection<? extends Thing>.

Le raisonnement est que un Collection<? extends Thing>pourrait contenir n'importe quel sous-type de Thing, et donc chaque élément se comportera comme un Thinglorsque vous effectuez votre opération. (Vous ne pouvez en fait rien ajouter à un Collection<? extends Thing>, car vous ne pouvez pas savoir au moment de l'exécution quel sous-type spécifique de Thingla collection contient.)

Cas 2: vous souhaitez ajouter des éléments à la collection.
Ensuite, la liste est un consommateur , vous devez donc utiliser un Collection<? super Thing>.

Le raisonnement ici est que, contrairement à Collection<? extends Thing>, Collection<? super Thing>peut toujours tenir quelque Thingsoit le type paramétré réel. Ici, vous ne vous souciez pas de ce qui est déjà dans la liste tant qu'il permettra Thingd'ajouter un; c'est ce qui ? super Thinggarantit.


142
J'essaie toujours de penser de cette façon: un producteur est autorisé à produire quelque chose de plus spécifique, donc s'étend , un consommateur est autorisé à accepter quelque chose de plus général, donc super .
Feuermurmel

10
Une autre façon de se souvenir de la distinction producteur / consommateur est de penser à une signature de méthode. Si vous avez une méthode doSomethingWithList(List list), vous consommez la liste et donc vous aurez besoin de covariance / étend (ou une liste invariante). D'un autre côté, si votre méthode est List doSomethingProvidingList, alors vous produisez la liste et vous aurez besoin de contravariance / super (ou d'une liste invariante).
Raman

3
@MichaelMyers: Pourquoi ne pouvons-nous pas simplement utiliser un type paramétré pour ces deux cas? Y a-t-il un avantage spécifique à utiliser des caractères génériques ici, ou est-ce juste un moyen d'améliorer la lisibilité similaire à, disons, l'utilisation de références à constcomme paramètres de méthode en C ++ pour signifier que la méthode ne modifie pas les arguments?
Chatterjee

7
@Raman, je pense que vous venez de le confondre. Dans doSthWithList (vous pouvez avoir List <? Super Thing>), puisque vous êtes un consommateur, vous pouvez utiliser super (rappelez-vous, CS). Cependant, c'est List <? étend Thing> getList () puisque vous êtes autorisé à retourner quelque chose de plus spécifique lors de la production (PE).
masterxilo

4
@AZ_ Je partage votre sentiment. Si une méthode obtient () de la liste, la méthode sera considérée comme un consommateur <T> et la liste est considérée comme un fournisseur; mais la règle de PECS est «du point de vue de la liste», donc «étend» est nécessaire. Ce devrait être GEPS: obtenir s'étend; mettre super.
Treefish Zhang

561

Les principes derrière cela en informatique sont appelés

  • Covariance: ? extends MyClass,
  • Contravariance: ? super MyClasset
  • Invariance / non-variance: MyClass

L'image ci-dessous devrait expliquer le concept. Photo gracieuseté: Andrey Tyukin

Covariance vs Contravariance


144
Salut tout le monde. Je suis Andrey Tyukin, je voulais juste confirmer que anoopelias & DaoWen m'ont contacté et ont obtenu ma permission d'utiliser le croquis, il est sous licence (CC) -BY-SA. Thx @ Anoop pour lui avoir donné une seconde vie ^^ @Brian Agnew: (sur "quelques votes"): C'est parce que c'est un croquis pour Scala, il utilise la syntaxe Scala et suppose une variance de site de déclaration, ce qui est assez différent de l'appel bizarre de Java -écart de site ... Je devrais peut-être écrire une réponse plus détaillée qui montre clairement comment ce croquis s'applique à Java ...
Andrey Tyukin

3
C'est l'une des explications les plus simples et les plus claires de la covariance et de la contravariance que j'aie jamais trouvées!
cs4r

@Andrey Tyukin Salut, je veux aussi utiliser cette image. Comment puis-je te contacter?
slouc

Si vous avez des questions sur cette illustration, nous pouvons en discuter dans le salon de
Andrey Tyukin


49

PECS (producteur extendset consommateur super)

mnémonique → Principe Get and Put.

Ce principe stipule que:

  • Utilisez un caractère générique étendu lorsque vous obtenez uniquement des valeurs d'une structure.
  • Utilisez un super caractère générique lorsque vous mettez uniquement des valeurs dans une structure.
  • Et n'utilisez pas de caractère générique lorsque vous obtenez et mettez.

Exemple en Java:

class Super {

    Object testCoVariance(){ return null;} //Covariance of return types in the subtype.
    void testContraVariance(Object parameter){} // Contravariance of method arguments in the subtype.
}

class Sub extends Super {

    @Override
    String testCoVariance(){ return null;} //compiles successfully i.e. return type is don't care(String is subtype of Object) 
    @Override
    void testContraVariance(String parameter){} //doesn't support even though String is subtype of Object

}

Principe de substitution de Liskov: si S est un sous-type de T, alors les objets de type T peuvent être remplacés par des objets de type S.

Dans le système de type d'un langage de programmation, une règle de frappe

  • covariant s'il préserve l'ordre des types (≤), qui ordonne les types de plus spécifiques à plus génériques;
  • contravariant s'il renverse cet ordre;
  • invariant ou non variant si aucun de ces deux critères ne s'applique.

Covariance et contravariance

  • Les types de données en lecture seule (sources) peuvent être covariants ;
  • les types de données en écriture seule (récepteurs) peuvent être contravariants .
  • Les types de données mutables qui agissent à la fois comme sources et comme récepteurs devraient être invariants .

Pour illustrer ce phénomène général, considérons le type de tableau. Pour le type Animal, nous pouvons faire le type Animal []

  • covariant : un chat [] est un animal [];
  • contravariant : un animal [] est un chat [];
  • invariant : un animal [] n'est pas un chat [] et un chat [] n'est pas un animal [].

Exemples Java:

Object name= new String("prem"); //works
List<Number> numbers = new ArrayList<Integer>();//gets compile time error

Integer[] myInts = {1,2,3,4};
Number[] myNumber = myInts;
myNumber[0] = 3.14; //attempt of heap pollution i.e. at runtime gets java.lang.ArrayStoreException: java.lang.Double(we can fool compiler but not run-time)

List<String> list=new ArrayList<>();
list.add("prem");
List<Object> listObject=list; //Type mismatch: cannot convert from List<String> to List<Object> at Compiletime  

plus d'exemples

caractère générique délimité (c'est-à-dire en direction de quelque part) : il existe 3 variantes différentes de caractères génériques:

  • In-variance / Non-variance: ?or ? extends Object- Unbounded générique . Il représente la famille de tous types. Utilisez lorsque vous obtenez et mettez.
  • Co-variance: ? extends T(la famille de tous les types qui sont des sous-types de T) - un caractère générique avec une limite supérieure . Test la classe supérieure de la hiérarchie d'héritage. Utilisez un extendscaractère générique lorsque vous obtenez uniquement des valeurs d'une structure.
  • Contre-variance: ? super T(la famille de tous les types qui sont des supertypes T) - un caractère générique avec une borne inférieure . Test la classe la plus basse de la hiérarchie d'héritage. Utilisez un supercaractère générique lorsque vous placez uniquement des valeurs dans une structure.

Remarque: le caractère générique ?signifie zéro ou une fois , représente un type inconnu. Le caractère générique peut être utilisé comme type de paramètre, jamais utilisé comme argument de type pour un appel de méthode générique, une création d'instance de classe générique (c'est-à-dire lorsqu'il est utilisé comme caractère générique cette référence non utilisée ailleurs dans un programme comme nous utilisons T)

entrez la description de l'image ici

class Shape { void draw() {}}

class Circle extends Shape {void draw() {}}

class Square extends Shape {void draw() {}}

class Rectangle extends Shape {void draw() {}}

public class Test {
 /*
   * Example for an upper bound wildcard (Get values i.e Producer `extends`)
   * 
   * */  

    public void testCoVariance(List<? extends Shape> list) {
        list.add(new Shape()); // Error:  is not applicable for the arguments (Shape) i.e. inheritance is not supporting
        list.add(new Circle()); // Error:  is not applicable for the arguments (Circle) i.e. inheritance is not supporting
        list.add(new Square()); // Error:  is not applicable for the arguments (Square) i.e. inheritance is not supporting
        list.add(new Rectangle()); // Error:  is not applicable for the arguments (Rectangle) i.e. inheritance is not supporting
        Shape shape= list.get(0);//compiles so list act as produces only

        /*You can't add a Shape,Circle,Square,Rectangle to a List<? extends Shape> 
         * You can get an object and know that it will be an Shape
         */         
    }
      /* 
* Example for  a lower bound wildcard (Put values i.e Consumer`super`)
* */
    public void testContraVariance(List<? super Shape> list) {
        list.add(new Shape());//compiles i.e. inheritance is supporting
        list.add(new Circle());//compiles i.e. inheritance is  supporting
        list.add(new Square());//compiles i.e. inheritance is supporting
        list.add(new Rectangle());//compiles i.e. inheritance is supporting
        Shape shape= list.get(0); // Error: Type mismatch, so list acts only as consumer
        Object object= list.get(0); // gets an object, but we don't know what kind of Object it is.

        /*You can add a Shape,Circle,Square,Rectangle to a List<? super Shape> 
        * You can't get an Shape(but can get Object) and don't know what kind of Shape it is.
        */  
    }
}

génériques et exemples


Hé, je voulais juste savoir ce que vous vouliez dire avec la dernière phrase: "Si vous pensez que mon analogie est fausse, veuillez mettre à jour". Voulez-vous dire si c'est éthiquement mauvais (ce qui est subjectif) ou si c'est faux dans le contexte de la programmation (ce qui est objectif: non, ce n'est pas faux)? Je voudrais le remplacer par un exemple plus neutre qui est universellement acceptable indépendamment des normes culturelles et des croyances éthiques; Si cela vous convient.
Neuron

enfin je pouvais l'obtenir. Belle explication.
Oleg Kuts

2
@Premraj,, In-variance/Non-variance: ? or ? extends Object - Unbounded Wildcard. It stands for the family of all types. Use when you both get and put.je ne peux pas ajouter d'élément à List <?> Ou List <? étend Object>, donc je ne comprends pas pourquoi cela peut être Use when you both get and put.
LiuWenbin_NO.

1
@LiuWenbin_NO. - Cette partie de la réponse est trompeuse. ?- le "caractère générique illimité" - correspond à l'opposé exact de l'invariance. Veuillez vous référer à la documentation suivante: docs.oracle.com/javase/tutorial/java/generics/… qui stipule: Dans le cas où le code doit accéder à la variable en tant que variable "in" et "out", faites ne pas utiliser de caractère générique. (Ils utilisent "in" et "out" comme synonymes de "get" et "put"). À l'exception de nullvous ne pouvez pas ajouter à une collection paramétrée avec ?.
mouselabs

29
public class Test {

    public class A {}

    public class B extends A {}

    public class C extends B {}

    public void testCoVariance(List<? extends B> myBlist) {
        B b = new B();
        C c = new C();
        myBlist.add(b); // does not compile
        myBlist.add(c); // does not compile
        A a = myBlist.get(0); 
    }

    public void testContraVariance(List<? super B> myBlist) {
        B b = new B();
        C c = new C();
        myBlist.add(b);
        myBlist.add(c);
        A a = myBlist.get(0); // does not compile
    }
}

Donc "? Étend B" doit être interprété comme "? B étend". C'est quelque chose que B étend afin d'inclure toutes les super classes de B jusqu'à Object, à l'exception de B lui-même. Merci pour le code!
Saurabh Patil

3
@SaurabhPatil Non, ? extends Bsignifie B et tout ce qui prolonge B.
asgs

24

Comme je l'explique dans ma réponse à une autre question, PECS est un dispositif mnémonique créé par Josh Bloch pour aider à se souvenir de P roducer extends, C onsumer super.

Cela signifie que lorsqu'un type paramétré transmis à une méthode produira des instances de T(elles en seront extraites d'une manière ou d'une autre), ? extends Tdevrait être utilisé, car toute instance d'une sous-classe de Test également un T.

Lorsqu'un type paramétré transmis à une méthode consommera des instances deT (elles lui seront transmises pour faire quelque chose), ? super Tdoit être utilisé car une instance de Tpeut légalement être transmise à toute méthode acceptant un certain type de T. Un Comparator<Number>pourrait être utilisé sur un Collection<Integer>, par exemple. ? extends Tne fonctionnerait pas, car un Comparator<Integer>ne pouvait pas opérer sur un Collection<Number>.

Notez qu'en général, vous ne devez utiliser ? extends Tet ? super Tpour les paramètres que d'une méthode. Les méthodes doivent simplement être utilisées Tcomme paramètre de type sur un type de retour générique.


1
Ce principe s'applique-t-il uniquement aux collections? Cela a du sens quand on essaie de le corréler avec une liste. Si vous pensez à la signature de tri (Liste <T>, Comparateur <? Super T>) ---> ici le Comparateur utilise super donc cela signifie qu'il est un consommateur dans le contexte PECS. Lorsque vous regardez par exemple l'implémentation: public int compare (Person a, Person b) {return a.age <b.age? -1: a.age == b.age? 0: 1; } J'ai l'impression que la personne ne consomme rien mais produit de l'âge. Cela me rend confus. Y a-t-il une faille dans mon raisonnement ou PECS ne s'applique qu'aux collections?
Fatih Arslan

24

En bref, trois règles faciles à retenir PECS:

  1. Utilisez le <? extends T>caractère générique si vous devez récupérer un objet de type Tdans une collection.
  2. Utilisez le <? super T>caractère générique si vous devez mettre des objets de typeT dans une collection.
  3. Si vous devez satisfaire les deux choses, eh bien, n'utilisez aucun caractère générique. Aussi simple que cela.

10

supposons cette hiérarchie:

class Creature{}// X
class Animal extends Creature{}// Y
class Fish extends Animal{}// Z
class Shark extends Fish{}// A
class HammerSkark extends Shark{}// B
class DeadHammerShark extends HammerSkark{}// C

Clarifions PE - Producer Extends:

List<? extends Shark> sharks = new ArrayList<>();

Pourquoi vous ne pouvez pas ajouter des objets qui étendent "Shark" dans cette liste? comme:

sharks.add(new HammerShark());//will result in compilation error

Étant donné que vous avez une liste qui peut être de type A, B ou C au moment de l'exécution , vous ne pouvez pas y ajouter d'objet de type A, B ou C car vous pouvez vous retrouver avec une combinaison non autorisée en java.
En pratique, le compilateur peut en effet voir au moment de la compilation que vous ajoutez un B:

sharks.add(new HammerShark());

... mais il n'a aucun moyen de savoir si lors de l'exécution, votre B sera un sous-type ou un supertype du type liste. Au moment de l'exécution, le type de liste peut être n'importe lequel des types A, B, C. Vous ne pouvez donc pas finir par ajouter HammerSkark (super type) dans une liste de DeadHammerShark par exemple.

* Vous direz: "OK, mais pourquoi ne puis-je pas y ajouter HammerSkark car c'est le plus petit type?". Réponse: C'est le plus petit que vous connaissez. Mais HammerSkark peut également être étendu par quelqu'un d'autre et vous vous retrouvez dans le même scénario.

Clarifions CS - Consumer Super:

Dans la même hiérarchie, nous pouvons essayer ceci:

List<? super Shark> sharks = new ArrayList<>();

Quoi et pourquoi vous pouvez ajouter à cette liste?

sharks.add(new Shark());
sharks.add(new DeadHammerShark());
sharks.add(new HammerSkark());

Vous pouvez ajouter les types d'objets ci-dessus car tout ce qui se trouve en dessous du requin (A, B, C) sera toujours des sous-types de tout ce qui se trouve au-dessus du requin (X, Y, Z). Facile à comprendre.

Vous ne pouvez pas ajouter de types au-dessus de Shark, car au moment de l' exécution, le type d'objet ajouté peut être plus élevé dans la hiérarchie que le type déclaré de la liste (X, Y, Z). Ce n'est pas permis.

Mais pourquoi vous ne pouvez pas lire cette liste? (Je veux dire que vous pouvez en extraire un élément, mais vous ne pouvez pas l'affecter à autre chose que l'Objet o):

Object o;
o = sharks.get(2);// only assignment that works

Animal s;
s = sharks.get(2);//doen't work

Au moment de l'exécution, le type de liste peut être de n'importe quel type au-dessus de A: X, Y, Z, ... Le compilateur peut compiler votre instruction d'affectation (qui semble correcte) mais, au moment de l' exécution, le type de s (Animal) peut être inférieur dans hiérarchie que le type déclaré de la liste (qui pourrait être Créature ou supérieur). Ce n'est pas permis.

Pour résumer

Nous utilisons <? super T>pour ajouter des objets de types égaux ou inférieurs Tau List. Nous ne pouvons pas en lire.
Nous utilisons <? extends T>pour lire des objets de types égaux ou inférieurs Tdans la liste. Nous ne pouvons pas y ajouter d'élément.


9

(ajout d'une réponse car jamais assez d'exemples avec des caractères génériques génériques)

       // Source 
       List<Integer> intList = Arrays.asList(1,2,3);
       List<Double> doubleList = Arrays.asList(2.78,3.14);
       List<Number> numList = Arrays.asList(1,2,2.78,3.14,5);

       // Destination
       List<Integer> intList2 = new ArrayList<>();
       List<Double> doublesList2 = new ArrayList<>();
       List<Number> numList2 = new ArrayList<>();

        // Works
        copyElements1(intList,intList2);         // from int to int
        copyElements1(doubleList,doublesList2);  // from double to double


     static <T> void copyElements1(Collection<T> src, Collection<T> dest) {
        for(T n : src){
            dest.add(n);
         }
      }


     // Let's try to copy intList to its supertype
     copyElements1(intList,numList2); // error, method signature just says "T"
                                      // and here the compiler is given 
                                      // two types: Integer and Number, 
                                      // so which one shall it be?

     // PECS to the rescue!
     copyElements2(intList,numList2);  // possible



    // copy Integer (? extends T) to its supertype (Number is super of Integer)
    private static <T> void copyElements2(Collection<? extends T> src, 
                                          Collection<? super T> dest) {
        for(T n : src){
            dest.add(n);
        }
    }

4

C'est la façon la plus claire et la plus simple pour moi de penser à l'extension vs super:

  • extendsest pour la lecture

  • superest pour écrire

Je trouve que "PECS" est une façon non évidente de penser aux choses concernant qui est le "producteur" et qui est le "consommateur". « PECS » est défini du point de vue de la collecte des données elle - même - la collection « consume » si les objets sont en cours d' écriture à (il consomme des objets de code d' appel), et « produit » si les objets sont lus à partir (il produit des objets vers un code appelant). C'est contraire à la façon dont tout le reste est nommé. Les API Java standard sont nommées du point de vue du code appelant, et non de la collection elle-même. Par exemple, une vue centrée sur la collection de java.util.List doit avoir une méthode nommée "receive ()" au lieu de "add ()" - après tout,l'élément, mais la liste elle-mêmereçoit l'élément.

Je pense qu'il est plus intuitif, naturel et cohérent de penser les choses du point de vue du code qui interagit avec la collection - le code "lit-il" ou "écrit-il" dans la collection? Ensuite, tout code écrit dans la collection serait le "producteur", et tout code lu dans la collection serait le "consommateur".


J'ai rencontré cette même collision mentale et j'aurais tendance à être d'accord, sauf que PECS ne spécifie pas la dénomination du code et que les limites de type elles - mêmes sont définies dans les déclarations de collection. De plus, en ce qui concerne la dénomination, vous avez souvent des noms pour produire / consommer des collections comme srcet dst. Donc, vous traitez à la fois du code et des conteneurs en même temps et j'ai fini par y penser de la même manière: «consommer du code» consomme à partir d'un conteneur producteur et «produire du code» produit pour un conteneur consommateur.
mouselabs

4

La "règle" PECS garantit simplement que ce qui suit est légal:

  • Consommation: ce qui ?est, il peut légalement se référer à T
  • Producteur: quoi qu'il en ?soit, il peut être légalement désigné par T

L'appariement typique dans le sens de List<? extends T> producer, List<? super T> consumers'assure simplement que le compilateur peut appliquer les règles de relation d'héritage "IS-A" standard. Si nous pouvions le faire légalement, cela pourrait être plus simple à dire <T extends ?>, <? extends T>(ou mieux encore à Scala, comme vous pouvez le voir ci-dessus, c'est [-T], [+T]. Malheureusement, le mieux que nous puissions faire est <? super T>, <? extends T>.

Quand j'ai rencontré cela pour la première fois et que je suis tombé en panne dans ma tête, la mécanique avait du sens, mais le code lui-même a continué à sembler déroutant pour moi - je pensais toujours "il semble que les limites ne devraient pas avoir besoin d'être inversées comme ça" - même si je était clair sur ce qui précède - qu'il s'agit simplement de garantir le respect des règles de référence standard.

Ce qui m'a aidé, c'est de le regarder en utilisant une affectation ordinaire comme analogie.

Considérez le code de jouet suivant (non prêt pour la production):

// copies the elements of 'producer' into 'consumer'
static <T> void copy(List<? extends T> producer, List<? super T> consumer) {
   for(T t : producer)
       consumer.add(t);
}

Pour illustrer cela en termes d'analogie d'affectation, consumerle ?caractère générique (type inconnu) est la référence - le "côté gauche" de l'affectation - et <? super T>garantit que tout ce qui ?est T"IS-A" ?- Tpeut lui être attribué, car ?est un super type (ou tout au plus le même type) que T.

Car producerle problème est le même, il est juste inversé: producerle ?caractère générique de (type inconnu) est le référent - le "côté droit" de l'affectation - et <? extends T>garantit que tout ce qui ?est, ?"IS-A" T- qu'il peut être attribué à unT , car il ?s'agit d'un sous-type (ou au moins du même type) que T.


2

Rappelez-vous ceci:

Consommateur souper (super); Un producteur agrandit l'usine de ses parents


1

En utilisant un exemple réel (avec quelques simplifications):

  1. Imaginez un train de marchandises avec des wagons de marchandises comme analogie avec une liste.
  2. Vous pouvez placer une cargaison dans un wagon de marchandises si la cargaison a la même taille ou une taille plus petite que le wagon de marchandises =<? super FreightCarSize>
  3. Vous pouvez décharger une cargaison d'un wagon de marchandises si vous avez suffisamment de place (plus que la taille de la cargaison) dans votre dépôt =<? extends DepotSize>

1

Covariance : accepter les sous-types
Contravariance : accepter les supertypes

Les types covariants sont en lecture seule, tandis que les types contravariants sont en écriture seule.


0

Regardons l'exemple

public class A { }
//B is A
public class B extends A { }
//C is A
public class C extends A { }

Les génériques vous permettent de travailler avec les types de manière dynamique et en toute sécurité

//ListA
List<A> listA = new ArrayList<A>();

//add
listA.add(new A());
listA.add(new B());
listA.add(new C());

//get
A a0 = listA.get(0);
A a1 = listA.get(1);
A a2 = listA.get(2);
//ListB
List<B> listB = new ArrayList<B>();

//add
listB.add(new B());

//get
B b0 = listB.get(0);

Problème

Étant donné que la collection Java est un type de référence en conséquence, nous avons les prochains problèmes:

Problème n ° 1

//not compiled
//danger of **adding** non-B objects using listA reference
listA = listB;

* Le générique de Swift n'a pas un tel problème car Collection est Value type[About] donc une nouvelle collection est créée

Problème n ° 2

//not compiled
//danger of **getting** non-B objects using listB reference
listB = listA;

La solution - Wildcards génériques

Le caractère générique est une fonctionnalité de type référence et ne peut pas être instancié directement

Solution n ° 1 <? super A> aka borne inférieure aka contravariance aka consommateurs garantit qu'il est exploité par A et toutes les superclasses, c'est pourquoi il est sûr d' ajouter

List<? super A> listSuperA;
listSuperA = listA;
listSuperA = new ArrayList<Object>();

//add
listSuperA.add(new A());
listSuperA.add(new B());

//get
Object o0 = listSuperA.get(0);

Solution n ° 2

<? extends A>aka limite supérieure aka covariance aka producteurs garantit qu'il est exploité par A et toutes les sous-classes, c'est pourquoi il est sûr d' obtenir et de lancer

List<? extends A> listExtendsA;
listExtendsA = listA;
listExtendsA = listB;

//get
A a0 = listExtendsA.get(0);

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.