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


Je suis tombé sur PECS (abréviation de Producteur extends et consommateur super) tout en lisant sur les génériques.

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


548
2018-04-27 17:16


origine


Réponses:


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


Supposons que vous ayez une méthode qui prenne comme paramètre une collection de choses, mais vous voulez qu'elle soit plus flexible que l'acceptation d'un Collection<Thing>.

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

Le raisonnement est qu'un Collection<? extends Thing> pourrait contenir n'importe quel sous-type de Thing, et donc chaque élément se comportera 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 spécifique sous-type de Thing la collection tient.)

Cas 2: Vous voulez ajouter des choses à la collection.
Ensuite, la liste est un consommateur, donc vous devriez utiliser un Collection<? super Thing>.

Le raisonnement ici est que contrairement à Collection<? extends Thing>, Collection<? super Thing> peut toujours tenir un Thing peu importe le type paramétré. Ici vous ne vous souciez pas de ce qui est déjà dans la liste tant que cela permettra Thing être ajouté; C'est quoi ? super Thing garanties.


632
2018-04-27 17:37



Les principes derrière cela en informatique sont nommés d'après

  • Covariance -? étend MyClass,
  • Contravariance -? Super MyClass et
  • Invariance / non-variance - MyClass

L'image ci-dessous devrait expliquer le concept.

Photo gracieuseté: Andrey Tyukin

Covariance vs Contravariance


444
2017-11-02 06:34



PECS (abréviation de "Producteur extends et consommateur super") peut être expliqué par: Obtenir et mettre en principe

Obtenir et mettre en principe (des génériques et des collections Java)

Il est dit,

  1. utiliser un étend wildcard quand tu ne fais que obtenir valeurs sur une structure
  2. utiliser un super générique quand tu ne fais que mettre valeurs dans une structure
  3. et n'utilise pas de joker lorsque vous à la fois obtenir et mettre.

Comprenons-le par exemple:

1. Pour Extends Wildcard (obtenir des valeurs i.e Producer extends)

Voici une méthode, qui prend une collection de nombres, convertit chacun à un doubleet les résume

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() utilisations de la méthode extends, tous les appels suivants sont légaux. Les deux premiers appels ne seraient pas légaux si l'extension n'était pas utilisée.

EXCEPTION : Toi ne peux rien mettre  dans un type déclaré avec un extends wildcard - 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 Wildcard (mettre des valeurs i.e Consumer super)

Voici une méthode, qui prend une collection de nombres et un int net 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() utilisations de la méthode super, tous les appels suivants sont légaux: Les deux derniers appels ne seraient pas légaux si le super n'était pas utilisé.

EXCEPTION : toi ne peut rien obtenir à partir d'un type déclaré avec un super wildcard - sauf pour une valeur de type Object, qui est un supertype de tout 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. Lorsque Get et Put, n'utilisez pas de joker

Quand tu les deux mettent valeurs dans et obtenir valeurs de la même structure, vous ne devrait pas utiliser un caractère générique.

public static double sumCount(Collection<Number> nums, int n) {
   count(nums, n);
   return sum(nums);
}

126
2018-01-25 13:39



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
    }
}

26
2017-10-07 18:15



PECS (producteur extends et consommateur super)

mnémonique → Get and Put principe.

Ce principe stipule que:

  • Utilisez un caractère générique extends lorsque vous obtenez uniquement des valeurs d'une structure.
  • Utilisez un super caractère générique lorsque vous ne mettez que des valeurs dans une structure.
  • Et n'utilisez pas de joker lorsque vous obtenez et mettez tous les deux.

En Java, les paramètres et les paramètres de type générique 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:  Tableaux sont covariants (dangereux) mais les génériques ne sont pas invariables (sûrs). Le 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  

plus d'exemples 

délimité(c'est-à-dire se diriger vers quelque part) wildcard : Il existe 3 différentes variantes de caractères génériques:

  • In-variance / non-variance: ? ou ? extends Object - Sans bornes Caractère générique Il représente la famille de tous les types. Utilisez quand vous obtenez et mettez tous les deux.
  • 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 le plus haut-la plus haute classe dans la hiérarchie d'héritage. Utilisez un extends wildcard quand vous seulement Obtenir valeurs hors d'une structure.
  • Contra-variance: ? super T (la famille de tous les types qui sont des supertypes de T) - un joker avec un borne inférieure. T est le inférieur-la plus haute classe dans la hiérarchie d'héritage. Utiliser un super wildcard quand vous seulement Mettre valeurs dans une structure.

Note: joker ? veux dire 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 une invocation 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 n'est pas utilisée ailleurs dans le programme 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


24
2017-07-26 06:33



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 Producteur extends, Cutilisateur super.

Cela signifie que lorsqu'un type paramétré est transmis à une méthode produire instances de T (ils seront récupérés d'une certaine manière), ? extends T devrait être utilisé, car toute instance d'une sous-classe de T est aussi un T.

Lorsqu'un type paramétré est transmis à une méthode consommer instances de T (ils seront passés à faire quelque chose), ? super T devrait être utilisé parce qu'une instance de T peut légalement être transmis à une méthode qui accepte un supertype de T. UNE Comparator<Number> pourrait être utilisé sur un Collection<Integer>, par exemple. ? extends T ne fonctionnerait pas, car un Comparator<Integer> ne pouvait pas fonctionner sur un Collection<Number>.

Notez qu'en général, vous ne devriez utiliser ? extends T et ? super T pour les paramètres de certaines méthodes. Les méthodes devraient simplement utiliser T en tant que paramètre de type sur un type de retour générique.


18
2018-04-27 17:32



En bref, facile à retenir PECS

  1. Utilisez le <? extends T> wildcard si vous avez besoin de récupérer l'objet de type T d'une collection.
  2. Utilisez le <? super T> wildcard si vous avez besoin de mettre des objets de type T dans une collection.
  3. Si vous avez besoin de satisfaire les deux choses, n'utilisez aucun caractère générique. Comme simple comme il est.

14
2017-11-13 06:51