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

j'ai rencontré PECS (abréviation de Producer extends and Consumer super ) en lisant sur generics.

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

570
demandé sur Lii 2010-04-27 21:16:40

11 réponses

tl;dr: "PECS" est du point de vue de la collection. Si vous êtes seulement tirer des articles d'une collection générique, il est un producteur et vous devez utiliser extends ; si vous êtes seulement articles de farce dans, Il est un consommateur et vous devez utiliser super . Si vous faites les deux avec la même collection, vous ne devriez pas utiliser extends ou super .


supposons que vous ayez une méthode qui prend comme paramètre un ensemble de choses, mais vous voulez qu'elle soit plus flexible que simplement accepter un Collection<Thing> .

Case 1: vous voulez parcourir la collection et faire des choses avec chaque article.

Ensuite, la liste est un producteur , donc vous devez utiliser un Collection<? extends Thing> .

le raisonnement est qu'un Collection<? extends Thing> pourrait contenir n'importe quelle sous-type de Thing , et donc chaque élément se comporte comme un Thing lorsque vous effectuez votre opération. (En fait, vous ne pouvez rien ajouter à un Collection<? extends Thing> , parce que vous ne pouvez pas savoir à l'exécution quel spécifique sous-type de Thing la collection tient.)

Case 2: vous voulez ajouter des choses à la collection.

Alors la liste est un consommateur , donc vous devriez l'utilisation d'un Collection<? super Thing> .

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

654
répondu Michael Myers 2015-05-29 16:39:36

Les principes derrière cette en Sciences de l'Informatique est nommé d'après

  • Covariance -? extend MyClass,
  • Contravariance -? super MyClass et
  • Invariance / non-Variance-MyClass

L'image ci-dessous devrait expliquer le concept.

Photo courtoisie: Andrey Tyukin

Covariance vs Contravariance

453
répondu anoopelias 2017-05-23 12:10:41

PECS (abréviation de producteur extends et consommateur super ") peut être expliqué par: Get and Put Principle

Get Et Put Principe (à Partir de Java Génériques et Collections)

indique le,

  1. utiliser un rallonge Joker quand vous seulement get valeurs hors d'une structure
  2. utiliser un Super Joker quand vous seulement mettre valeurs dans une structure
  3. et n'utilisez pas de Joker lorsque vous obtenez et mettez .

comprenons-le par l'exemple:

1. Pour Extend Wildcard (get values I. e producteur extends )

Voici une méthode, qui prend une collection de nombres, convertit chaque un double , et résume les

public static double sum(Collection<? extends Number> nums) {
   double s = 0.0;
   for (Number num : nums) 
      s += num.doubleValue();
   return s;
}

appelons la méthode:

List<Integer>ints = Arrays.asList(1,2,3);
assert sum(ints) == 6.0;
List<Double>doubles = Arrays.asList(2.78,3.14);
assert sum(doubles) == 5.92;
List<Number>nums = Arrays.<Number>asList(1,2,2.78,3.14);
assert sum(nums) == 8.92;

depuis, sum() la méthode utilise extends , tous les appels suivants sont légaux. Les deux premiers appels ne seraient pas légaux si les prolongations n'étaient pas utilisées.

EXCEPTION : vous ne pouvez rien mettre dans un type déclaré avec un extends Joker-sauf pour la valeur null , qui appartient à chaque type de référence:

List<Integer> ints = new ArrayList<Integer>();
ints.add(1);
ints.add(2);
List<? extends Number> nums = ints;
nums.add(null);  // ok
assert nums.toString().equals("[1, 2, null]");

2. Pour Super Joker (put values I. e consommateur super )

Voici une méthode, qui prend une collection de nombres et un int n , et met le premier n entiers, à partir de zéro, dans la collection:

public static void count(Collection<? super Integer> ints, int n) {
    for (int i = 0; i < n; i++) ints.add(i);
}

appelons la méthode:

List<Integer>ints = new ArrayList<Integer>();
count(ints, 5);
assert ints.toString().equals("[0, 1, 2, 3, 4]");
List<Number>nums = new ArrayList<Number>();
count(nums, 5); nums.add(5.0);
assert nums.toString().equals("[0, 1, 2, 3, 4, 5.0]");
List<Object>objs = new ArrayList<Object>();
count(objs, 5); objs.add("five");
assert objs.toString().equals("[0, 1, 2, 3, 4, five]");

depuis, count() la méthode utilise super , tous les appels suivants sont légaux: Les deux derniers appels ne seraient pas légaux si super n'était pas utilisé.

EXCEPTION : vous ne peut rien obtenir d'un type déclaré avec un super Joker-sauf pour une valeur de type Object , qui est un supertype de chaque type de référence:

List<Object> objs = Arrays.<Object>asList(1,"two");
List<? super Integer> ints = objs;
String str = "";
for (Object obj : ints) str += obj.toString();
assert str.equals("1two");

3. Quand les deux obtenir et mettre, n'utilisez pas de Joker

chaque fois que vous à la fois mettre valeurs dans et obtenir valeurs de la même structure, vous ne doit pas utiliser de jointure .

public static double sumCount(Collection<Number> nums, int n) {
   count(nums, n);
   return sum(nums);
}
132
répondu Prateek 2015-05-08 23:28:24
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
    }
}
27
répondu Gab 2013-10-07 18:15:13

PECS (Producteur de extends et de la Consommation super )

mnémonique → obtenir et mettre Principe.

ce principe stipule que:

  • utilisez un joker extends lorsque vous obtenez seulement des valeurs hors d'une structure.
  • utilisez un Super Joker lorsque vous mettez seulement des valeurs dans une structure.
  • et n'utilisez pas de Joker quand vous obtenez et mettez.

en Java, les paramètres et les paramètres génériques de type ne prennent pas en charge l'héritage comme suit.

class Super {
    void testCoVariance(Object parameter){} // method Consumes the Object
    Object testContraVariance(){ return null;} //method Produces the Object
}

class Sub extends Super {
    @Override
    void testCoVariance(String parameter){} //doesn't support eventhough String is subtype of Object

    @Override
    String testContraVariance(){ return null;} //compiles successfully i.e. return type is don't care 
}

principe de substitution de Liskov: les tableaux sont covariants(dangereux) mais les génériques ne sont pas i.e. invariants(sûrs). i.e. principe de Substitution ne fonctionne pas avec les types paramétrés, ce qui signifie qu'il est illégal d'écrire.

Covariant signifie simplement si X est sous-type de Y puis X[] sera également sous-type de Y[] .

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  

autres exemples

bornded (i.e. heading toward somewhere) wildcard : il existe 3 saveurs différentes de wildcards:

  • Dans de variance/Non-variance: ? ou ? extends Object - Illimité "de 1519410920" Joker. Il représente la famille de tous les types. Utilisez quand vous obtenez et mis.
  • Co-variance: ? extends T (la famille de tous les types qui sont des sous - types de T ) - un joker avec un limite supérieure . T est la supérieure - la classe la plus dans la hiérarchie de l'héritage. Utilisez un joker extends lorsque vous n'avez que les valeurs Get d'une structure.
  • Contra-variance: ? super T (la famille de tous les types qui sont des supertypes de T ) - un joker avec un limite inférieure . T est le inférieur - la classe la plus dans la hiérarchie de l'héritage. Utilisez un joker super lorsque vous mettez seulement des valeurs dans une structure.

Note: le caractère générique ? signifie zéro ou une fois , représente type inconnu. Le Joker peut être utilisé comme le type d'un paramètre, jamais utilisé comme argument de type pour une invocation de méthode générique, une création d'instance de classe générique.(c'est-à-dire lorsqu'on utilise le caractère de remplacement qui n'est pas utilisé ailleurs dans le programme comme nous utilisons T )

enter image description here

class Shape { void draw() {}}

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

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

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

public class TestContraVariance {
 /*
   * 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<? extends 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

25
répondu Premraj 2018-06-15 01:08:19

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

Cela signifie que lorsqu'un type paramétré passé à une méthode produire cas de T (ils seront récupérées à partir d'une certaine façon), ? extends T doit être utilisé, puisque toute instance d'une sous-classe de T est aussi un T .

quand un type paramétré étant passé à une méthode sera consommer instances de T (ils seront passés à elle pour faire quelque chose), ? super T doit être utilisé parce qu'une instance de T peut légalement être passé à toute méthode qui accepte un certain supertype de T . Un Comparator<Number> pourrait être utilisé sur un Collection<Integer> , exemple. ? extends T ne fonctionnerait pas, parce qu'un Comparator<Integer> ne pourrait pas fonctionner sur un Collection<Number> .

notez qu'en général vous ne devez utiliser ? extends T et ? super T que pour les paramètres d'une méthode. Les méthodes doivent juste utiliser T comme paramètre de type sur un type de retour Générique.

20
répondu ColinD 2017-08-25 12:34:45

en bref facile à se rappeler PECS

  1. utilisez le Joker <? extends T> si vous avez besoin de récupérer l'objet de tapez T d'une collection.
  2. utilisez le Joker <? super T> si vous avez besoin de mettre des objets de type T dans collection.
  3. si vous avez besoin de satisfaire les deux choses, n'utilisez pas de Joker. Comme simple qu'il soit.
16
répondu Pradeep Kr Kaushal 2014-11-13 06:51:24

(ajouter une réponse parce que jamais assez d'exemples avec des caractères génériques wildcards)

       // 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
répondu Andrejs 2017-05-17 18:12:22

rappelez-vous ceci:

"151900920 du Consommateur" manger cène (super); Producteur s'étend de ses parents usine

2
répondu Jason 2018-04-29 06:13:16

assumons 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

puisque vous avez une liste qui peut être de type A, B ou C à l'exécution , vous ne pouvez pas y ajouter d'objet de type A, B ou C parce que vous pouvez finir avec une combinaison qui est pas autorisés en java.

dans la pratique, le compilateur peut effectivement voir à l'heure du compilateur que vous ajoutez un B:

sharks.add(new HammerShark());

...mais il n'a aucun moyen de dire si à l'exécution, Votre B sera un sous-type ou supertype du type de liste. À l'exécution, le type de liste peut être n'importe lequel des types A, B, C. Ainsi, vous ne pouvez pas finir par ajouter HammerSkark (super type) dans une liste de DeadHammerShark par exemple.

*Vous allez dire: "OK, mais pourquoi ne puis-je pas ajouter HammerSkark en elle puisque c'est le plus petit type?". Réponse: C'est le plus petit vous savoir. Acheter HammerSkark peut être augmenté 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<>();

Ce qui et pourquoi vous peut ajouter à cette liste?

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

vous pouvez ajouter les types d'objets ci-dessus parce que tout ce qui est en dessous de requin(A,B,C) sera toujours des sous-types de tout ce qui est au-dessus de requin (X,Y,Z). Facile à comprendre.

Vous ne peut pas ajouter des types ci-dessus de Requin, parce que au moment de l'exécution le type de l'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 autorisé.

mais pourquoi ne pouvez-vous pas lire à partir de cette liste? (Je veux dire que vous peut en retirer 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

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

pour résumer

nous utilisons <? super T> pour ajouter des objets de types égaux ou inférieurs à T dans la liste. nous ne pouvons pas lire de il.

nous utilisons <? extends T> pour lire les objets des types égaux ou inférieurs à T de la liste. nous ne pouvons pas y ajouter d'élément.

1
répondu Daniel 2018-09-23 17:42:34

les caractères génériques peuvent être utilisés de trois façons:"

              - Upper bound Wildcard  ( ? extends Type ).

              - Lower bound Wildcard  ( ? super Type ) .

              - Unbounded Wildcard    ( ? ) .

pour les besoins de cette discussion, il est utile de penser aux variables comme fournissant l'une des deux fonctions:

                      - In Variable

                              An "in" variable serves up data to the code. 
                              Imagine a copy method with two arguments: 
                                      copy(src, dest)
                              The src argument provides the data to be copied, so it is the "in" parameter.
                      - Out Variable

                              An "out" variable holds data for use elsewhere. In the copy example, 
                                      copy(src, dest)
                              the dest argument accepts data, so it is the "out" parameter.

              An "in" variable is defined with an upper bounded wildcard, using the extends keyword.
              An "out" variable is defined with a lower bounded wildcard, using the super keyword.
              In the case where the "in" variable can be accessed using methods defined in the Object class, use an unbounded wildcard.
              In the case where the code needs to access the variable as both an "in" and an "out" variable, do not use a wildcard.

                      class NaturalNumber 
                      {

                              private int i;

                              public NaturalNumber(int i)
                              { 
                                      this.i = i;
                              }
                      }

                          class EvenNumber extends NaturalNumber 
                          {

                              public EvenNumber(int i) 
                              { 
                                      super(i);
                              }
                          }

              Consider the following code:

                      List<EvenNumber> le = new ArrayList<>();
                      List<? extends NaturalNumber> ln = le;
                      ln.add(new NaturalNumber(35));  // compile-time error


                      You can add null.
                      You can invoke clear.
                      You can get the iterator and invoke remove.
                      You can capture the wildcard and write elements that you've read from the list.
0
répondu Ali Yeganeh 2018-10-01 13:12:23