Domanda

Mi sono imbattuto in PECS (abbreviazione di produttore extends e Consumer super ) durante la lettura su farmaci generici.

Can qualcuno mi spieghi come utilizzare PECS per risolvere confusione tra extends e super?

È stato utile?

Soluzione

tl; dr: "PECS" è dal punto di vista della collezione. Se sei solo tirando elementi da un insieme generico, è un produttore e si dovrebbe utilizzare extends; se si è solo ripieno articoli in, è un consumatore e si dovrebbe utilizzare super. Se si fa sia con la stessa collezione, non si dovrebbe usare sia extends o super.


Supponiamo di avere un metodo che accetta come parametro un insieme di cose, ma si desidera che sia più flessibile di un semplice accettare un Collection<Thing>.

Caso 1:. Si vuole passare attraverso la raccolta e fare le cose con ogni elemento
Poi la lista è un produttore , quindi è necessario utilizzare un Collection<? extends Thing>.

Il ragionamento è che un Collection<? extends Thing> poteva contenere qualsiasi sottotipo di Thing, e quindi ogni elemento si comporterà come un Thing quando si esegue l'operazione. (In realtà non si può aggiungere nulla ad un Collection<? extends Thing>, perché non si può sapere in fase di esecuzione che specifica sottotipo di Thing collezione detiene.)

Caso 2: Si desidera aggiungere le cose alla raccolta
. Poi la lista è un dei consumatori , quindi è necessario utilizzare un Collection<? super Thing>.

Il ragionamento è che a differenza di Collection<? extends Thing>, Collection<? super Thing> può sempre tenere un Thing non importa quale sia il tipo parametrico effettivo è. Qui non si cura ciò che è già nella lista fintanto che permetterà una Thing da aggiungere; questo è ciò che garantisce ? super Thing.

Altri suggerimenti

I principi alla base di questo in informatica si chiama

  • covarianza: ? extends MyClass,
  • controvarianza: ? super MyClass e
  • invarianza / non-varianza: MyClass

L'immagine qui sotto dovrebbe spiegare il concetto. Foto per gentile concessione: Andrey Tyukin

Covariance vs controvarianza

PECS (Produttore extends e super dei consumatori)

mnemonico ? Get e principio di Put.

Questo principio afferma che:

  • Usa un estende jolly quando si arriva solo i valori di una struttura.
  • Usa un super jolly quando solo si mette i valori in una struttura.
  • E non utilizzare un carattere jolly quando si sia get e put.

Esempio in 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 sostituzione principio: se S è un sottotipo di T, allora oggetti di tipo T può essere sostituite con oggetti di tipo S.

Nel sistema tipo di un linguaggio di programmazione, una regola di digitazione

  • covariante se conserva l'ordinamento dei tipi (=), che ordina tipi da più specifico più generico;
  • contravariant se si inverte questo ordinamento;
  • invariante o nonvariant se nessuno di questi casi.

Covarianza e controvarianza

  • Sola lettura tipi di dati (fonti) possono essere covariante ;
  • I tipi di dati di sola scrittura (lavandini) può essere contravariant .
  • i tipi di dati mutabili che fungono da entrambe le sorgenti e pozzi deve essere invariante .

Per illustrare questo fenomeno generale, considerare il tipo di matrice. Per l'animale tipo possiamo fare l'Animal tipo []

  • covariante : un gatto [] è un animale [];
  • contravariant : un animale [] è un gatto [];
  • invariante :. An Animal [] non è un gatto [] e un gatto [] non è un animale []

Esempi di 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  

altri esempi

limitato (vale a dire in direzione da qualche parte.) jolly : Ci sono 3 diversi gusti di jolly:

  • In-varianza / non-varianza: ? o ? extends Object - Unbounded jolly. Si distingue per la famiglia di tutti i tipi. Utilizzare quando entrambi get e put.
  • Co-varianza: ? extends T (la famiglia di tutti i tipi che sono sottotipi di T) - un jolly con una limite superiore . T è il in alto di classe -La maggior parte nella gerarchia di ereditarietà. Utilizzare un jolly extends quando solo i valori di una struttura.
  • Contra-varianza: ? super T (la famiglia di tutti i tipi che sono supertipi di T) - un jolly con una BASSA legato . T è il inferiore di classe -La maggior parte nella gerarchia di ereditarietà. Utilizzare un carattere jolly super quando solo Mettere i valori in una struttura.

Nota: mezzi ? jolly zero o una volta , rappresenta un tipo sconosciuto. Il carattere jolly può essere utilizzata come tipo di un parametro, mai usato come argomento tipo per una chiamata di metodo generico, una creazione dell'istanza classe generica. (Vale a dire quando jolly usate tale riferimento non utilizzato altrove nel programma come usiamo T)

 entrare descrizione dell'immagine qui

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

generici e esempi

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

Come spiego nel mia risposta a un'altra domanda, PECS è un dispositivo mnemonico creato da Josh Bloch per ricordare P roducer extends, C super CONSUMATORI.

  

Questo significa che quando un tipo parametrico viene passata ad un metodo sarà prodotti istanze di T (saranno recuperati da esso in qualche modo), ? extends T dovrebbe essere utilizzato, dal momento che qualsiasi istanza di una sottoclasse di T è anche un T.

     

Quando un tipo parametrico viene passata ad un metodo consumare istanze di T (che sarà passato ad esso per fare qualcosa), dovrebbe essere utilizzato ? super T perché un'istanza di T può essere legalmente passato a qualsiasi metodo che accetta un certo supertipo di T. Un Comparator<Number> potrebbe essere utilizzata su un Collection<Integer>, per esempio. ? extends T non funzionerebbe, perché un Comparator<Integer> non potrebbe funzionare su un Collection<Number>.

Si noti che in genere si dovrebbe usare solo ? extends T e ? super T per i parametri di un metodo. I metodi devono semplicemente usare T come parametro tipo su un tipo di ritorno generico.

In poche parole, tre semplici regole da ricordare PECS:

  1. Utilizzare il carattere jolly <? extends T> se avete bisogno di recuperare l'oggetto di digitare T da una collezione.
  2. Utilizzare il carattere jolly <? super T> se avete bisogno di mettere oggetti di tipo T in una collezione.
  3. Se avete bisogno di soddisfare entrambe le cose, bene, non utilizzare alcun carattere jolly. Come semplice.

(l'aggiunta di una risposta perché mai abbastanza esempi con caratteri jolly Generics)

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

Supponiamo questa gerarchia:

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 chiarire PE - Produttore Estende:

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

Perché non è possibile aggiungere oggetti che si estendono "Shark" in questa lista? come:

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

Dal momento che si dispone di un elenco che può essere di tipo A, B o C in fase di esecuzione , non è possibile aggiungere qualsiasi oggetto di tipo A, B o C in essa, perché si può finire con una combinazione che non è consentito in java. pratica
In, il compilatore può infatti vedere in compiletime che si aggiunge un B:

sharks.add(new HammerShark());

... ma non ha alcun modo di sapere se in fase di esecuzione, il vostro B sarà un sottotipo o supertipo del tipo di elenco. In fase di esecuzione il tipo di lista può essere uno qualsiasi dei tipi A, B, C. Così non si può finire l'aggiunta di HammerSkark (tipo super) in un elenco di DeadHammerShark per esempio.

* Voi direte: "OK, ma perché non posso aggiungere HammerSkark in esso dal momento che è il tipo più piccolo?". Risposta: E 'il più piccolo il know. Ma HammerSkark può essere esteso anche da qualcun altro e si finisce nello stesso scenario.

chiarire CS Let - Super consumatori:

Nella stessa gerarchia possiamo provare questo:

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

Che cosa e perché si possono aggiungere a questa lista?

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

È possibile aggiungere i tipi di oggetti di cui sopra, perché nulla al di sotto di squalo (A, B, C) sarà sempre sottotipi di qualsiasi cosa al di sopra di squalo (X, Y, Z). Facile da capire.

non possono aggiungere tipi sopra Shark, perché in fase di esecuzione il tipo di oggetto aggiunto può essere maggiore in gerarchia rispetto al tipo dichiarato della lista (X, Y, Z ). Ciò non è consentito.

Ma perché non si può leggere da questa lista? (Voglio dire che si può ottenere un elemento fuori di esso, ma non è possibile assegnare a qualcosa di diverso da Object o):

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

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

In fase di runtime, il tipo di elenco può essere di qualsiasi tipo sopra A: X, Y, Z, ... Il compilatore può compilare la sua dichiarazione di assegnazione (che sembra corretto), ma, in fase di esecuzione il tipo di s (Animale) può essere inferiore della gerarchia rispetto al tipo dichiarato della lista (che potrebbe essere Creatura, o superiore ). Ciò non è consentito.

Per riassumere

Usiamo <? super T> per aggiungere oggetti di tipo uguale o inferiore a T nella lista. Non possiamo leggere da esso.
Usiamo <? extends T> a leggere gli oggetti di tipo uguale o inferiore a T dalla lista. non possiamo aggiungere elementi ad esso.

Ricordate questo:

  

mangiare consumatori cena (super); Produttore estende di fabbrica dei suoi genitori

covarianza : accettare sottotipi
controvarianza : accettare supertipi

tipi covarianti sono di sola lettura, mentre i tipi controvarianti sono di sola scrittura.

Utilizzando esempio di vita reale (con alcune semplificazioni):

  1. Immaginate un treno merci con vagoni merci in analogia con una lista.
  2. Puoi put un carico in un carro merci, se il carico ha uguale o minore dimensione del carro merci = <? super FreightCarSize>
  3. Puoi scarico un carico da un carro merci, se si dispone di abbastanza posto (più rispetto alla dimensione del carico) nel vostro deposito = <? extends DepotSize>
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top