Question

Je suis tombé sur PECS (abréviation de Producteur extends et consommateurs super ) lors de la lecture sur les médicaments génériques.

Quelqu'un peut-il me expliquer comment utiliser PECS de confusion entre résolution et extends super?

Était-ce utile?

La solution

tl; dr: "PECS" est du point de vue de la collection. Si vous êtes uniquement tirer des éléments d'une collection générique, il est un producteur et vous devez utiliser extends; si vous êtes uniquement bourrage produits 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 soit extends ou super.


Supposons que vous avez une méthode qui prend comme paramètre une collection de choses, mais vous voulez qu'il soit plus souple que juste d'accepter un Collection<Thing>.

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

Le raisonnement est qu'une Collection<? extends Thing> pourrait tenir un sous-type de Thing, et donc chaque élément se comportera comme un Thing lorsque vous effectuez votre opération. (Vous pouvez pas vraiment ajouter quelque chose à un Collection<? extends Thing>, parce que vous ne pouvez pas savoir à l'exécution qui spécifique sous-type de la collection Thing détient.)

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

Le raisonnement est ici que, contrairement Collection<? extends Thing>, Collection<? super Thing> peut toujours tenir une Thing peu importe ce que le type paramétrées réelle est. Ici, vous ne vous inquiétez pas ce qui est déjà dans la liste tant qu'elle permettra à un Thing à ajouter; c'est ce que garantit ? super Thing.

Autres conseils

Les principes de cette en informatique est appelé

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

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

Covariance vs Contravariance

PECS (Producteur extends et super consommateurs)

mnémotechnique ? Get et Put principe.

Ce principe stipule que:

  • Utilisez un caractère générique se prolonge lorsque vous obtenez seulement des valeurs sur une structure.
  • Utilisez un joker super quand vous ne mettez des valeurs dans une structure.
  • Et ne pas utiliser un caractère générique lorsque vous deux get et put.

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

}

Liskov principe de substitution: si S est un sous-type de T, puis des objets de type T peut être remplacés par des objets de type S.

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

  • covariant si elle préserve l'ordre des types (=), qui commande les types de plus spécifiques à plus générique;
  • contravariante si elle renverse cet ordre;
  • invariant ou si aucun de non variant ceux-ci s'applique.

Covariance et contravariance

  • Lecture seule types de données (sources) peuvent être covariant ;
  • types de données en écriture seule (puits) peut être contravariante .
  • types de données mutables qui agissent comme les deux sources et des puits devrait être invariant .

Pour illustrer ce phénomène général, considérer le type de tableau. Pour l'animal type, on peut rendre l'animal de type []

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

Java Exemples:

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

borné (i.e. cap vers quelque part.) caractères génériques : Il y a 3 saveurs différentes de jokers:

  • Dans la variance / non-variance: ? ou ? extends Object - Unbounded Wildcard. Il représente la famille de tous les types. A utiliser lorsque vous deux get et put.
  • Co-variance: ? extends T (la famille de tous les types qui sont sous-types de T) - un caractère générique avec limite supérieure . T est la classe supérieure dans la hiérarchie -La plupart de l'héritage. Utilisez un lorsque vous ne wildcard extends Obtenir valeurs hors d'une structure.
  • Contre-variance: ? super T (la famille de tous les types qui sont supertypes de T) - un caractère générique avec une limite inférieure . T est la classe bas dans la hiérarchie -La plupart de l'héritage. Utilisez un caractère générique de super lorsque vous ne Mettre valeurs dans une structure.

Remarque: des moyens de ? génériques zéro ou une fois , représente un type inconnu. Le caractère générique peut être utilisé comme le type d'un 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. (Lorsque générique utilisé cette référence pas utilisé dans ailleurs dans le programme comme nous utilisons T)

 entrer image description 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

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

Comme je l'explique dans ma réponse une autre question, PECS est un mnémotechnique créé par Josh Bloch à l'aide se rappeler P roducer extends, C onsumer super.

  

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

     

Quand un type paramétrées étant passé à une méthode consomment cas de T (ils seront transmis à à faire quelque chose), ? super T doit être utilisé car une instance de T peut légalement être passé à toute méthode qui accepte une supertype de T. Un Comparator<Number> pourrait être utilisé sur un Collection<Integer>, par exemple. ? extends T ne fonctionnerait pas, parce qu'un Comparator<Integer> ne pouvait pas fonctionner sur un Collection<Number>.

Notez que généralement vous ne devez utiliser ? extends T et ? super T pour les paramètres de quelque méthode. Les méthodes doivent simplement utiliser T comme paramètre de type sur un type générique de retour.

En un mot, trois règles faciles à retenir PECS:

  1. Utilisez le caractère générique <? extends T> si vous avez besoin de récupérer l'objet de de type T d'une collection.
  2. Utilisez le caractère générique <? super T> si vous avez besoin de mettre des objets de type T dans une collection.
  3. Si vous avez besoin pour satisfaire les deux choses, eh bien, ne pas utiliser de caractères génériques. Comme simple que cela.

(ajouter une réponse parce que jamais assez d'exemples avec des jokers 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);
        }
    }

Supposons que 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

Let clarifier PE - Producteur Étend:

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

Pourquoi vous ne pouvez pas ajouter des objets qui Extend « 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 lors de l'exécution , vous ne pouvez pas ajouter un objet de type A, B ou C parce que vous pouvez vous retrouver avec une combinaison qui n'est pas autorisé en java.
Dans la pratique, le compilateur peut en effet voir à compiletime que vous ajoutez 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 du type super-type de liste. Lors de l'exécution du type de liste peut être l'un des types A, B, C. Vous ne pouvez pas finir par ajouter HammerSkark (type super) dans une liste de DeadHammerShark par exemple.

* Vous direz: « OK, mais pourquoi ne puis-je ajouter HammerSkark en elle, car il est le type le plus petit? ». Réponse: Il est le plus petit savoir. Mais HammerSkark peut être prolongé aussi par quelqu'un d'autre et vous finissez dans le même scénario.

Clarifions CS - Super consommateur:

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

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

Qu'est-ce 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 le requin ci-dessous (A, B, C) sera toujours sous-types de quoi que ce soit au-dessus de requin (X, Y, Z). Facile à comprendre.

ne pouvez pas ajouter types ci-dessus Shark, parce que lors 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 autorisé.

Mais pourquoi vous ne pouvez pas lire cette liste? (Je veux dire que vous pouvez obtenir un élément hors de lui, mais vous ne pouvez pas l'attribuer à autre chose que Object o):

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

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

A l'exécution, le type de liste peut être tout type ci-dessus A: X, Y, Z, ... Le compilateur peut compiler votre instruction d'affectation (ce qui semble correct) mais, lors de 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 une créature ou plus ). Ce n'est pas autorisé.

En résumé

Nous utilisons <? super T> pour ajouter des objets de types inférieurs ou égaux à T dans la liste. Nous ne pouvons pas lire elle.
Nous utilisons <? extends T> pour lire des objets de types inférieurs ou égaux à T de la liste. Nous ne pouvons pas ajouter l'élément à lui.

Rappelez-vous ceci:

  

manger des consommateurs souper (super); Producteur étend l'usine de ses parents

Covariance : accepter les sous-types
Contravariance : accepter supertypes

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

Exemple d'utilisation de la vie réelle (avec quelques simplifications):

  1. Imaginez un train de marchandises avec des wagons de marchandises comme analogie à une liste.
  2. Vous pouvez mettre une cargaison dans un wagon de marchandises si la cargaison a ou même de plus petite taille que la voiture de fret = <? super FreightCarSize>
  3. Vous pouvez unload une cargaison d'un wagon de marchandises si vous avez assez lieu (plus de la taille de la cargaison) dans votre dépôt = <? extends DepotSize>
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top