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

Resuelto peakit asked hace 14 años • 16 respuestas

Me encontré con PECS (abreviatura de Productor extendsy Consumidorsuper ) mientras leía sobre genéricos.

¿Alguien puede explicarme cómo usar PECS para resolver la confusión entre extendsy super?

peakit avatar Apr 28 '10 00:04 peakit
Aceptado

tl;dr: "PECS" es desde el punto de vista de la colección. Si solo estás extrayendo elementos de una colección genérica, es un productor y debes usar extends; si solo estás metiendo artículos, es un consumidor y debes usarlo super. Si haces ambas cosas con la misma colección, no deberías usar ni extendsni super.


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

Caso 1: Quieres revisar la colección y hacer cosas con cada artículo.
Entonces la lista es un productor , por lo que deberías usar un archivo Collection<? extends Thing>.

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

Caso 2: Quieres agregar cosas a la colección.
Entonces la lista es un consumidor , por lo que deberías usar un Collection<? super Thing>.

El razonamiento aquí es que, a diferencia de Collection<? extends Thing>, Collection<? super Thing>siempre puede contener a Thingsin importar cuál sea el tipo parametrizado real. Aquí no le importa lo que ya esté en la lista, siempre y cuando permita Thingagregar un; esto es lo que ? super Thinggarantiza.

Michael Myers avatar Apr 27 '2010 17:04 Michael Myers

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

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

La siguiente imagen debería explicar el concepto. Imagen cortesía de Andrei Tyukin.

Covarianza vs Contravarianza

anoopelias avatar Nov 02 '2013 06:11 anoopelias

PECS es un dispositivo mnemotécnico en los genéricos de Java que nos ayuda a recordar qué palabra clave usar al especificar límites en parámetros de tipo genérico.

1 Productor extiende:

  • Se aplica a tipos de retorno y variables con límites superiores.
  • Representa un productor de valores.
  • Le permite devolver un subtipo más específico del tipo declarado (covariante).
  • La covarianza es una relación jerárquica, es decir, una relación "es-un": el gato como animal "es-un".

2 Súper Consumidor:

  • Se aplica a parámetros de entrada y variables con límites inferiores.
  • Representa un consumidor de valores.
  • Le permite aceptar un supertipo más general del tipo declarado (contravariante).
  • Contravarianza Inversión de la relación de subtipo: Animal que "puede retener" Cat.

3. Tipos invariantes/no variantes

  • (sin límites) tienen relaciones fijas y no se benefician de PECS.
  • Relación fija, es decir, el tipo debe permanecer exactamente como se especifica, sin ninguna flexibilidad para aceptar subtipos o supertipos: es decir, donde tanto los tipos de entrada como los de salida son fijos.

El Principio de Sustitución de Liskov (LSP) establece que “ los objetos de un programa deben ser reemplazables con instancias de sus subtipos sin alterar la corrección de ese programa ”.

ingrese la descripción de la imagen aquí Imagen original

comodín acotado (es decir, que se dirige hacia algún lugar) : hay 3 tipos diferentes de comodines :

  • Covarianza: ? extends T(Reinado de Tdescendientes): un comodín con un límite superior . Tes la clase más alta en la jerarquía de herencia. Utilice un extendscomodín cuando solo obtenga valores de una estructura. Los límites superiores promueven la sustitución segura de subtipos.
    Por ejemplo, si T extiende Animal, T puede ser el propio Animal o cualquiera de sus subtipos como Gato, Perro, etc., pero no puede ser un tipo "superior" que Animal, como Objeto.
  • Contravarianza: ? super T(Reinado del Tantepasado): un comodín con un límite inferior . Tes la clase más baja en la jerarquía de herencia. Utilice un supercomodín cuando solo coloque valores en una estructura. Los límites inferiores promueven la sustitución segura de supertipos.
  • Con variación/Sin variación: ?o ? extends Object- Comodín ilimitado . Representa la familia de todo tipo. Úselo cuando obtenga y ponga.

Nota: el comodín ?significa cero o una vez y representa un tipo desconocido . El comodín se puede usar como tipo de parámetro, nunca como argumento de tipo para una invocación de método genérico, una creación de instancia de clase genérica (es decir, cuando se usa un comodín, esa referencia no se usa en ningún otro lugar del programa como lo usamos nosotros T).

ingrese la descripción de la imagen aquí

 import java.util.ArrayList;
import java.util.List;

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 {

    public static void main(String[] args) {
        //? extends Shape i.e. can use any sub type of Shape, here Shape is Upper Bound in inheritance hierarchy
        List<? extends Shape> intList5 = new ArrayList<Shape>();
        List<? extends Shape> intList6 = new ArrayList<Cricle>();
        List<? extends Shape> intList7 = new ArrayList<Rectangle>();
        List<? extends Shape> intList9 = new ArrayList<Object>();//ERROR.


        //? super Shape i.e. can use any super type of Shape, here Shape is Lower Bound in inheritance hierarchy
        List<? super Shape> inList5 = new ArrayList<Shape>();
        List<? super Shape> inList6 = new ArrayList<Object>();
        List<? super Shape> inList7 = new ArrayList<Circle>(); //ERROR.

        //-----------------------------------------------------------
        Circle circle = new Circle();
        Shape shape = circle; // OK. Circle IS-A Shape

        List<Circle> circles = new ArrayList<>();
        List<Shape> shapes = circles; // ERROR. List<Circle> is not subtype of List<Shape> even when Circle IS-A Shape

        List<? extends Circle> circles2 = new ArrayList<>();
        List<? extends Shape> shapes2 = circles2; // OK. List<? extends Circle> is subtype of List<? extends Shape>


        //-----------------------------------------------------------
        Shape shape2 = new Shape();
        Circle circle2= (Circle) shape2; // OK. with type casting

        List<Shape> shapes3 = new ArrayList<>();
        List<Circle> circles3 = shapes3; //ERROR. List<Circle> is not subtype of  List<Shape> even Circle is subetype of Shape

        List<? super Shape> shapes4 = new ArrayList<>();
        List<? super Circle> circles4 = shapes4; //OK.
    }

    
    
    /*
     * Example for an upper bound wildcard (Get values i.e Producer `extends`)
     *
     * */
    public void testCoVariance(List<? extends Shape> list) {
        list.add(new Object());//ERROR
        list.add(new Shape()); //ERROR
        list.add(new Circle()); // ERROR
        list.add(new Square()); // ERROR
        list.add(new Rectangle()); // ERROR
        Shape shape= list.get(0);//OK 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 Object());//ERROR
        list.add(new Shape());//OK
        list.add(new Circle());//OK
        list.add(new Square());//OK
        list.add(new Rectangle());//OK
        Shape shape= list.get(0); // ERROR. Type mismatch, so list acts only as consumer
        Object object= list.get(0); //OK 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.
         */
    }
}

Más ejemplos de codificación
Visualizando el concepto

Premraj avatar Jul 26 '2015 06:07 Premraj
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
    }
}
Gab avatar Oct 07 '2013 18:10 Gab

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

  1. Utilice el <? extends T>comodín si necesita recuperar un objeto de tipo Tde una colección.
  2. Utilice el <? super T>comodín si necesita colocar objetos de tipo Ten una colección.
  3. Si necesita satisfacer ambas cosas, no utilice ningún comodín. Tan sencillo como eso.
Pradeep Kr Kaushal avatar Nov 13 '2014 06:11 Pradeep Kr Kaushal