Pregunta

Me encontré con PECS (abreviatura de extends productor y el consumidor super ), mientras que leer sobre los genéricos.

Puede alguien me explique cómo utilizar PECS a la confusión entre extends determinación y super?

¿Fue útil?

Solución

tl; dr: "PECS" es desde el punto de vista de la colección. Si usted es solamente tirando objetos de una colección genérica, es un productor y se debe utilizar extends; si usted es solamente relleno en artículos, que es un consumidor y se debe utilizar super. Si lo hace tanto con la misma colección, no se debe utilizar ya sea extends o super.


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

Caso 1:. ¿Quieres ir a través de la colección y hacer cosas con cada elemento
A continuación, la lista es un productor , por lo que debe utilizar un Collection<? extends Thing>.

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

Caso 2: Si desea añadir cosas a la colección
. A continuación, la lista es un consumidor , por lo que debe utilizar un Collection<? super Thing>.

El razonamiento aquí es que a diferencia de Collection<? extends Thing>, Collection<? super Thing> siempre se puede mantener una Thing sin importar el tipo parametrizado es real. Aquí no importa lo que ya está en la lista, siempre que permitirá una Thing que se añade; esto es lo que garantiza ? super Thing.

Otros consejos

Los principios detrás de esta en la informática se llama

  • covarianza: ? extends MyClass,
  • contravarianza: ? super MyClass y
  • La invariancia / no varianza: MyClass

La siguiente imagen debe explicar el concepto. Foto cortesía: Andrey Tyukin

Covarianza vs contravarianza

PECS (extends Productor y Consumidor super)

mnemotécnica ? Get y Put principio.

Este principio establece que:

  • Utilice un comodín se extiende cuando se consigue solamente los valores de una estructura.
  • Usar un super comodines cuando sólo valores pone en una estructura.
  • Y no utilizar un comodín cuando ambos get y put.

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

}

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

Dentro del sistema de tipo de un lenguaje de programación, una regla escribiendo

  • covariante si preserva el ordenamiento de los tipos (=), que ordena tipos de más específica a más genérico;
  • contravariant si se invierte este orden;
  • invariante o no variante, si ninguna de las situaciones anteriores.

Covarianza y contravarianza

  • Sólo lectura tipos de datos (fuentes) pueden ser covariante ;
  • Tipos de datos de sólo escritura (sumideros) puede ser contravariant .
  • Tipos de datos mutables que actúan como fuentes y sumideros debe ser invariante .

Para ilustrar este fenómeno general, tenga en cuenta el tipo de matriz. Para el animal tipo podemos hacer que el tipo de animal []

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

Los 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

acotada (es decir, en dirección a alguna parte.) comodín : Hay 3 sabores diferentes de comodines:

  • Dentro de la varianza / No varianza: ? o ? extends Object - ilimitada Comodín. Es sinónimo de la familia de todos los tipos. Uso que cuando ambos get y put.
  • Co-varianza: ? extends T (la familia de todos los tipos que son subtipos de T) - un comodín con un límite superior . T es la superior clase -la mayoría en la jerarquía de herencia. Utilizar un comodín extends cuando sólo Obtener 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 menor clase -la mayoría en la jerarquía de herencia. Utilizar un comodín super cuando sólo Poner valores en una estructura.

Nota: medios ? comodín cero o una vez , representa un tipo desconocido. El comodín se puede utilizar como el tipo de un parámetro, nunca utilizado como un argumento de tipo para una invocación de método genérico, una creación de la instancia de clase genérica. (Es decir, cuando comodín utilizado que la referencia no se utiliza en en el programa en otro lugar como usamos T)

 introducir descripción de la imagen aquí

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.
        */  
    }
}

genéricos y ejemplos

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

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

  

Esto significa que cuando un tipo parametrizado se pasa a un método voluntad productos casos de T (que serán recuperados de él de alguna manera), ? extends T debe utilizarse, ya que cualquier instancia de una subclase de T es también un T.

     

Cuando un tipo parametrizado se pasa a un método consumen casos de T (que será pasado a él para hacer algo), ? super T se debe utilizar porque una instancia de T legalmente se puede pasar a cualquier método que acepta alguna supertipo de T. A Comparator<Number> podría ser utilizado en un Collection<Integer>, por ejemplo. ? extends T no funcionaría, porque un Comparator<Integer> no podría funcionar en un Collection<Number>.

Tenga en cuenta que por lo general sólo se debe utilizar ? extends T y ? super T para los parámetros de algún método. Métodos simplemente deben utilizar T como el parámetro de tipo de un tipo de retorno genérico.

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

  1. Utilice el comodín <? extends T> si necesita recuperar el objeto de escriba T de una colección.
  2. Utilice el comodín <? super T> si usted necesita para poner los objetos de tipo T en una colección.
  3. Si tiene que satisfacer ambas cosas, así, no utilice ningún comodín. Como de simple.

(añadiendo una respuesta porque no suficientes ejemplos con comodines genéricos)

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

Vamos a suponer que esta jerarquía:

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

Vamos a aclarar PE - Productor Extiende:

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

¿Por qué no se puede añadir objetos que se extienden "Tiburón" en esta lista? como:

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

Dado que usted tiene una lista que puede ser de tipo A, B o C en tiempo de ejecución , no se puede agregar cualquier objeto de tipo A, B o C en ella, ya que puede terminar con una combinación que no está permitido en java.
En la práctica, el compilador de hecho puede ver en tiempo de compilación que se agrega un B:

sharks.add(new HammerShark());

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

* Usted dirá: "OK, pero ¿por qué no puedo añadir HammerSkark en ella, ya que es el tipo más pequeño?". Respuesta: Es el más pequeño conocimientos. Pero HammerSkark puede extenderse también por alguien más y se acaba en el mismo escenario.

Vamos a aclarar CS - Consumidor Súper:

En la misma jerarquía que podemos probar esto:

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

¿Qué y por qué puede añadir a esta lista?

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

Puede agregar los tipos anteriores de los objetos porque nada por debajo de tiburón (A, B, C) siempre será subtipos de nada por encima de tiburón (X, Y, Z). Fácil de comprender.

no pueden añadir tipos anteriormente Shark, porque en tiempo de ejecución el tipo de objeto añadido puede ser más alto en la jerarquía que el tipo declarado de la lista (X, Y, Z ). Esto no está permitido.

Pero ¿por qué no se puede leer de esta lista? (Quiero decir que usted puede conseguir un elemento fuera de él, pero no se puede asignar un valor distinto de Object o nada):

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

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

En tiempo de ejecución, el tipo de lista puede ser cualquier tipo anterior A: X, Y, Z, ... El compilador puede compilar su sentencia de asignación (que parece correcta), pero, en tiempo de ejecución el tipo de s (Animal) puede ser de menos rango que el tipo declarado de la lista (que podría ser la criatura, o más alto ). Esto no está permitido.

Para resumir

Utilizamos <? super T> para añadir objetos de tipos iguales o por debajo de T en la lista. No podemos leer desde ella.
Utilizamos <? extends T> para leer objetos de tipo igual o por debajo de T de la lista. No se puede agregar el elemento a la misma.

Recuerde esto:

  

comer Consumidor cena (super); Productor extiende la fábrica de su padre

covarianza : aceptar subtipos
contravarianza : aceptar supertipos

tipos Covariantes son de sólo lectura, mientras que los tipos contravariant son de sólo escritura.

Uso de ejemplo de la vida real (con algunas simplificaciones):

  1. Imagine un tren de mercancías con vagones de carga como analogía a una lista.
  2. Usted puede put una carga en un vagón de carga si la carga ha el mismo o menor tamaño que el vagón de carga = <? super FreightCarSize>
  3. Usted puede descarga una carga de un vagón de carga si tiene lugar suficiente (mayor que el tamaño de la carga) en su depósito de <? extends DepotSize> =
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top