11 Pregunta: ¿Qué es PECS (Producer Extends Consumer Super)?

pregunta creada en Tue, Apr 5, 2016 12:00 AM

Me encontré con PECS (abreviatura de Producer extends y Consumer super ) mientras leía sobre genéricos.

¿Puede alguien explicarme cómo usar PECS para resolver la confusión entre extends y super?

    
652
  1. Una muy buena explicación con un ejemplo @ youtube.com/watch?v=34oiEq9nD0M&feature=youtu.be&t=1630 que explica super parte, pero da una idea de otro.
    2019-01-20 06: 22: 36Z
11 Respuestas                              11                         

tl; dr: "PECS" es desde el punto de vista de la colección. Si está solo extrayendo artículos de una colección genérica, es un productor y debe usar extends; Si está solo rellenando artículos, es un consumidor y debe usar super. Si hace ambas cosas con la misma colección, no debe usar ni extends ni super.


Supongamos que tienes un método que toma como parámetro una colección de cosas, pero quieres que sea más flexible que solo aceptar un Collection<Thing>.

Caso 1: desea revisar la colección y hacer cosas con cada elemento.
Entonces la lista es un productor , por lo que debe usar un Collection<? extends Thing>.

El razonamiento es que un Collection<? extends Thing> podría contener cualquier subtipo de Thing y, por lo tanto, cada elemento se comportará como un Thing cuando realice su operación. (En realidad, no puede agregar nada a un Collection<? extends Thing>, porque no puede saber en tiempo de ejecución qué subtipo específico de Thing mantiene la colección.)

Caso 2: desea agregar elementos a la colección.
Entonces la lista es un consumidor , por lo que debe usar un Collection<? super Thing>.

El razonamiento aquí es que, a diferencia de Collection<? extends Thing>, Collection<? super Thing> siempre puede contener un Thing sin importar cuál sea el tipo real parametrizado. Aquí no le importa lo que ya está en la lista, siempre que permita que se agregue un Thing; Esto es lo que garantiza ? super Thing.

    
753
2015-05-29 16: 39: 36Z
  1. Siempre trato de pensarlo de esta manera: un productor puede producir algo más específico, por lo tanto, se extiende , un consumidor puede aceptar algo más general, por lo tanto, super .
    2013-05-07 13: 11: 56Z
  2. Otra forma de recordar la distinción entre productor /consumidor es pensar en una firma de método. Si tiene un método doSomethingWithList(List list), está consumiendo la lista y, por lo tanto, necesitará covarianza /extensión (o una Lista invariante). Por otra parte, si su método es List doSomethingProvidingList, entonces está produciendo la Lista y necesitará contravarianza /super (o una Lista invariante).
    2014-01-24 19: 20: 58Z
  3. @ MichaelMyers: ¿Por qué no podemos simplemente usar un tipo parametrizado para estos dos casos? ¿Existe alguna ventaja específica de usar caracteres comodín aquí, o es simplemente un medio para mejorar la legibilidad similar a, digamos, usar referencias a const como parámetros del método en C ++ para indicar que el método no modifica los argumentos?
    2014-05-24 06: 27: 26Z
  4. @ Raman, creo que simplemente lo confundiste. En doSthWithList (puede tener Lista &lt ;? super Thing >), ya que usted es un consumidor, puede usar super (recuerde, CS). Sin embargo, es Lista &lt ;? extiende Cosa > getList () ya que se le permite devolver algo más específico al producir (PE).
    2014-05-27 19: 08: 07Z
  5. @ Chatterjee: la razón para usar comodines es la flexibilidad. Al declarar que un parámetro es de tipo Colección &lt ;? cosa estupenda > le das thLa persona que llama tiene más flexibilidad, ya que puede invocar su método no solo con una Colección < Cosa > como argumento, pero también con una colección < SomeSupertypeOfThing > como un argumento.
    2016-02-11 13: 40: 46Z

Los principios detrás de esto en informática se llaman

  • Covarianza: ? extends MyClass,
  • Contravarianza: ? super MyClass y
  • Invarianza /no-varianza: MyClass

La imagen de abajo debería explicar el concepto.

Imagen cortesía: Andrey Tyukin

Covariance vs Contravariance

    
504
2019-01-19 10: 22: 39Z
  1. Hola a todos. Soy Andrey Tyukin, solo quería confirmar que anopelias & DaoWen se contactó conmigo y obtuvo mi permiso para usar el boceto, está licenciado en (CC) -BY-SA. Thx @ Anoop por darle una segunda vida ^^ @Brian Agnew: (con "pocos votos"): Eso se debe a que es un boceto para Scala, usa la sintaxis de Scala y asume una variación de sitio de declaración, que es muy diferente a la extraña llamada de Java varianza del sitio ... Tal vez debería escribir una respuesta más detallada que muestre claramente cómo se aplica este bosquejo a Java ...
    2014-06-15 23: 11: 38Z
  2. ¡Esta es una de las explicaciones más sencillas y claras sobre la Covarianza y la Contravariedad que he encontrado!
    2017-05-01 12: 35: 52Z
  3. @ Andrey Tyukin Hola, también quiero usar esta imagen. ¿Cómo puedo contactarte?
    2017-06-02 08: 06: 53Z
  4. Si tiene alguna pregunta sobre esta ilustración, podemos analizarla en la sala de chat: chat.stackoverflow.com/rooms/145734/…
    2017-06-02 17: 16: 17Z
  5. 2018-02-19 01: 12: 15Z

PECS (Productor extends y Consumer super)

mnemotécnica → Principio de Get and Put.

Este principio establece que:

  • Use un comodín extendido cuando solo obtiene valores de una estructura.
  • Use un super comodín cuando solo ponga valores en una estructura.
  • Y no use un comodín cuando los dos se ponen y ponen.

Ejemplo 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

}

Principio de sustitución de Liskov: si S es un subtipo de T, los objetos de tipo T pueden ser reemplazado por objetos de tipo S.

Dentro del sistema de tipos de un lenguaje de programación, una regla de escritura

  • covariante si conserva el orden de los tipos (≤), que ordena tipos de más específicos a más genéricos;
  • contravariante si revierte este ordenamiento;
  • invariante o no variante si ninguno de estos se aplica.

Covarianza y contravarianza

  • Los tipos de datos de solo lectura (fuentes) pueden ser covariant ;
  • los tipos de datos de solo escritura (sumideros) pueden ser contravariantes .
  • Los tipos de datos mutables que actúan como fuentes y sumideros deben ser invariantes .

Para ilustrarEn este fenómeno general, consideremos el tipo de matriz. Para el tipo Animal podemos hacer el tipo Animal []

  • covariante : un gato [] es un animal [];
  • contravariante : un animal [] es un gato [];
  • invariante : un animal [] no es un gato [] y un gato [] no es un animal [].

Ejemplos de 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  

más ejemplos

acotado (es decir, en dirección a algún lugar) comodín : hay 3 tipos diferentes de comodines:

  • In-variance /Non-variance: ? o ? extends Object - Unbounded Wildcard. Es sinónimo de la familia de todo tipo. Úselo cuando ambos obtengan y pongan.
  • Covarianza: ? extends T (la familia de todos los tipos que son subtipos de T) - un comodín con un límite superior . T es la clase más superior en la jerarquía de herencia. Utilice un comodín extends cuando solo obtenga los valores de una estructura.
  • Contra-varianza: ? super T (la familia de todos los tipos que son supertipos de T) - un comodín con un límite inferior . T es la clase más inferior en la jerarquía de herencia. Utilice un comodín super cuando solo Ponga los valores en una estructura.

Nota: el comodín ? significa cero o una vez , representa un tipo desconocido. El comodín se puede usar como el tipo de un parámetro, nunca se usa como un argumento de tipo para una invocación de método genérico, una creación de instancia de clase genérica. p>

 introduce la descripción de la imagen aquí

 T

genéricos y ejemplos

    
34
2019-04-13 08: 39: 41Z
  1. Oye, solo quería saber a qué te referías con la última frase: "Si crees que mi analogía es incorrecta, por favor actualiza". ¿Quiere decir si es éticamente incorrecto (que es subjetivo) o si es incorrecto en el contexto de la programación (que es objetivo: no, no es incorrecto)? Me gustaría reemplazarlo con un ejemplo más neutral que sea universalmente aceptable, independientemente de las normas culturales y las creencias éticas; Si eso está bien contigo.
    2018-04-29 06: 12: 53Z
  2. por fin pude obtenerlo. Buena explicación.
    2019-04-12 12: 36: 19Z
  3. @ Premraj,
    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.
            */  
        }
    }
    
    , no puedo agregar un elemento a la Lista <? > o Listar &lt ;? extiende Object &gt ;, por lo que no entiendo por qué puede ser In-variance/Non-variance: ? or ? extends Object - Unbounded Wildcard. It stands for the family of all types. Use when you both get and put..
    2019-05-17 04: 05: 08Z
 Use when you both get and put     
28
2013-10-07 18: 15: 13Z
  1. Entonces "? extiende B" se debe interpretar como "? B extiende". Es algo que B extiende, por lo que incluiría todas las súper clases de B hasta Objeto, excluyendo B en sí. ¡Gracias por el código!
    2016-05-30 03: 47: 48Z
  2. @ SaurabhPatil No,
    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
        }
    }
    
    significa B y cualquier cosa que se extienda B.
    2016-09-28 06: 16: 13Z

Como explico en mi respuesta a otra pregunta, PECS es un dispositivo mnemotécnico creado por Josh Bloch para ayudar a recordar P roducer ? extends B, C onsumer extends.

  

Esto significa que cuando un tipo parametrizado que se pasa a un método producirá instancias de super (se recuperarán de él de alguna manera), se debe usar T, ya que cualquier instancia de una subclase de ? extends T es también un T.

     

Cuando un tipo parametrizado que se pasa a un método consumirá instancias de T (se le pasarán para hacer algo), se debe usar T porque una instancia de ? super T se puede pasar legalmente a Cualquier método que acepte algún supertipo de T. Un T podría usarse en un Comparator<Number>, por ejemplo. Collection<Integer> no funcionaría, porque un ? extends T no podría operar en un Comparator<Integer>.

Tenga en cuenta que, en general, solo debe usar Collection<Number> y ? extends T para los parámetros de algún método. Los métodos solo deben usar ? super T como parámetro de tipo en un tipo de retorno genérico.

    
22
2017-08-25 12: 34: 45Z
  1. ¿Este principio solo se aplica a las colecciones? Tiene sentido cuando uno trata de correlacionarlo con una lista. Si piensa en la firma del tipo (Lista < T >, Comparator &lt ;? super T >) --- > aquí el Comparador usa super, lo que significa que es un consumidor en el contexto de PECS. Cuando observa la implementación, por ejemplo, como: public int compare (Person a, Person b) {return a.age < b.age? -1: a.age == b.age? 0: 1; } Siento que la persona no consume nada sino que produce edad. Eso me confunde. ¿Hay algún error en mi razonamiento o solo PECS se aplica a las colecciones?
    2019-02-07 14: 26: 04Z

En pocas palabras, tres reglas fáciles de recordar PECS:

  1. Utilice el comodín T si necesita recuperar un objeto de escriba <? extends T> de una colección.
  2. Utilice el comodín T si necesita colocar objetos del tipo <? super T> en una colección.
  3. Si necesita satisfacer ambas cosas, bueno, no use ningún comodín. Como tan simple como eso.
19
2019-01-19 10: 29: 56Z

(agregar una respuesta porque nunca hay suficientes ejemplos con comodines genéricos)

 T     
7
2017-05-17 18: 12: 22Z

Supongamos esta jerarquía:

 
       // 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);
        }
    }

Aclaremos PE: el productor se extiende:

 
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

¿Por qué no puedes agregar objetos que extiendan "Tiburón" en esta lista? como:

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

Ya que tiene una lista que puede ser de tipo A, B o C en tiempo de ejecución , no puede agregar ningún objeto de tipo A, B o C en ella porque puede terminar con una combinación Eso no está permitido en java.
En la práctica, el compilador puede ver en tiempo de compilación que agrega una B:

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

... pero no tiene forma de saber si, en tiempo de ejecución, su B será un subtipo o supertipo del tipo de lista. En el tiempo de ejecución, el tipo de lista puede ser cualquiera de los tipos A, B, C. Por lo tanto, no puede terminar agregando HammerSkark (supertipo) en una lista de DeadHammerShark, por ejemplo.

* Dirás: "Está bien, pero ¿por qué no puedo agregar HammerSkark ya que es el tipo más pequeño?". Respuesta: Es el más pequeño que conoces. Comprar a HammerSkark también puede ser extendido por otra persona y terminas en el mismo escenario.

Clarifiquemos CS - Super consumidor:

En la misma jerarquía podemos intentar esto:

 
sharks.add(new HammerShark());

¿Qué y por qué puede agregar a esta lista?

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

Puedes agregar los tipos de objetos anteriores porque cualquier cosa debajo del tiburón (A, B, C) siempre será subtipos de cualquier cosa sobre el tiburón (X, Y, Z). Fácil de entender.

Usted no puede agregar tipos por encima de Shark, porque en tiempo de ejecución el tipo de objeto agregado puede ser mayor en hjerarquía que el tipo declarado de la lista (X, Y, Z). Esto no está permitido.

¿Pero por qué no puedes leer de esta lista? (Quiero decir que puedes sacar un elemento de él, pero no puedes asignarlo a otro objeto que no sea el Objeto o):

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

En el tiempo de ejecución, el tipo de lista puede ser cualquier tipo por encima de A: X, Y, Z, ... El compilador puede compilar su declaración de asignación (lo que parece correcto) pero, en tiempo de ejecución el tipo de s (Animal) puede tener una jerarquía más baja que el tipo declarado de la lista (que podría ser Creature, o superior) ). Esto no está permitido.

Para resumir

Usamos

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

Animal s;
s = sharks.get(2);//doen't work
para agregar objetos de tipos iguales o inferiores a T en la lista. No podemos leer de . Usamos <? super T> para leer objetos de tipos iguales o debajo de T de la lista. No podemos agregarle un elemento.

    
5
2018-09-23 17: 42: 34Z

Recuerda esto:

  

El consumidor come cena (super); El productor extiende la fábrica de sus padres

    
2
2018-04-29 06: 13: 16Z

Covarianza : acepta subtipos
Contravarianza : acepta supertipos

Los tipos covariantes son de solo lectura, mientras que los tipos contravariantes son de solo escritura.

    
1
2019-02-07 15: 33: 37Z

Ejemplo de uso de la vida real (con algunas simplificaciones):

  1. Imagine un tren de carga con vagones de carga como analogía a una lista.
  2. Puede poner una carga en un vagón de carga si la carga tiene el mismo o menor tamaño que el vagón de carga = <? extends T>
  3. Puede descargar una carga de un vagón de carga si tiene lugar suficiente (más que el tamaño de la carga) en su depósito = <? super FreightCarSize>
0
2018-11-20 13: 47: 41Z
<? extends DepotSize>
fuente colocada aquí