Che cos'è PECS (Producer Extends Consumer Super)?


729

Mi sono imbattuto in PECS (acronimo di Producer extendsand Consumersuper ) mentre leggevo i farmaci generici.

Qualcuno può spiegarmi come utilizzare PECS per risolvere la confusione tra extendse super?


3
Un'ottima spiegazione con un esempio @ youtube.com/watch?v=34oiEq9nD0M&feature=youtu.be&t=1630 che spiega la superparte, ma dà un'idea di un'altra.
Lupchiazoem,

Risposte:


844

tl; dr: "PECS" è dal punto di vista della collezione. Se stai solo estraendo oggetti da una collezione generica, è un produttore e dovresti usare extends; se stai solo riempiendo oggetti, è un consumatore e dovresti usarlo super. Se si fa sia con la stessa collezione, non si dovrebbe usare uno extendso super.


Supponiamo di avere un metodo che prende come parametro una raccolta di cose, ma che vuoi che sia più flessibile che accettare semplicemente un Collection<Thing>.

Caso 1: vuoi passare attraverso la raccolta e fare le cose con ogni oggetto.
Quindi l'elenco è un produttore , quindi dovresti usare a Collection<? extends Thing>.

Il ragionamento è che a Collection<? extends Thing>potrebbe contenere qualsiasi sottotipo di Thing, e quindi ogni elemento si comporterà come Thingquando esegui la tua operazione. (In realtà non è possibile aggiungere nulla a a Collection<? extends Thing>, perché non è possibile sapere in fase di esecuzione quale sottotipo specifico della Thingraccolta contiene.)

Caso 2: vuoi aggiungere elementi alla raccolta.
Quindi l'elenco è un consumatore , quindi dovresti usare a Collection<? super Thing>.

Il ragionamento qui è che a differenza di Collection<? extends Thing>, Collection<? super Thing>può sempre tenere una Thingqualunque cosa sia il tipo con parametri effettivi. Qui non ti interessa ciò che è già nella lista purché permetta Thingdi aggiungere un; questo è ciò che ? super Thinggarantisce.


142
Cerco sempre di pensarci in questo modo: un produttore può produrre qualcosa di più specifico, quindi si estende , un consumatore può accettare qualcosa di più generale, quindi super .
Feuermurmel

10
Un altro modo per ricordare la distinzione produttore / consumatore è pensare a una firma del metodo. Se hai un metodo doSomethingWithList(List list), stai consumando l'elenco e quindi avrai bisogno di covarianza / estensione (o di un elenco invariante). D'altra parte, se il tuo metodo è List doSomethingProvidingList, allora stai producendo la Lista e avrai bisogno di contraddizione / super (o di una Lista invariante).
Raman,

3
@MichaelMyers: Perché non possiamo semplicemente usare un tipo con parametri per entrambi questi casi? C'è qualche vantaggio specifico nell'utilizzare i caratteri jolly qui, o è solo un mezzo per migliorare la leggibilità simile, ad esempio, all'utilizzo di riferimenti a constcome parametri di metodo in C ++ per indicare che il metodo non modifica gli argomenti?
Chatterjee,

7
@Raman, penso che tu l'abbia appena confuso. In doSthWithList (puoi avere List <? Super Thing>), poiché sei un consumatore, puoi usare super (ricorda, CS). Tuttavia, è List <? estende Thing> getList () poiché ti è permesso di restituire qualcosa di più specifico durante la produzione (PE).
masterxilo,

4
@AZ_ Condivido il tuo sentimento. Se un metodo ottiene () dall'elenco, il metodo verrebbe considerato un consumatore <T> e l'elenco verrà considerato un fornitore; ma la regola di PECS è "dal punto di vista dell'elenco", quindi si richiede "estende". Dovrebbe essere GEPS: get extends; mettere super.
Treefish Zhang

561

I principi dietro questo in informatica sono chiamati

  • Covarianza: ? extends MyClass,
  • Contravarianza: ? super MyClasse
  • Invarianza / non-varianza: MyClass

L'immagine sotto dovrebbe spiegare il concetto. Immagine gentilmente concessa : Andrey Tyukin

Covarianza vs Contravarianza


144
Ciao a tutti. Sono Andrey Tyukin, volevo solo confermare che le anoopelie e DaoWen mi hanno contattato e ottenuto il mio permesso di utilizzare lo schizzo, è concesso in licenza in (CC) -BY-SA. Thx @ Anoop per avergli dato una seconda vita ^^ @Brian Agnew: (su "pochi voti"): Questo perché è uno schizzo per Scala, usa la sintassi Scala e assume la varianza del sito di dichiarazione, che è molto diversa dalla strana chiamata di Java varianza interna ... Forse dovrei scrivere una risposta più dettagliata che mostri chiaramente come questo schizzo si applica a Java ...
Andrey Tyukin,

3
Questa è una delle spiegazioni più semplici e chiare per Covarianza e Contravarianza che io abbia mai trovato!
cs4r,

@Andrey Tyukin Ciao, anch'io voglio usare questa immagine. Come posso contattarti?
slouc

Se hai domande su questa illustrazione, possiamo discuterne nella chat: chat.stackoverflow.com/rooms/145734/…
Andrey Tyukin,


49

PECS (produttore extendse consumatore super)

mnemonico → Principio Get and Put.

Questo principio afferma che:

  • Utilizzare un carattere jolly esteso quando si ottengono valori solo da una struttura.
  • Utilizzare un super jolly quando si inseriscono valori solo in una struttura.
  • E non usare un carattere jolly quando entrambi ottieni e metti.

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

}

Principio di sostituzione di Liskov: se S è un sottotipo di T, allora gli oggetti di tipo T possono essere sostituiti con oggetti di tipo S.

All'interno del sistema di tipi di un linguaggio di programmazione, una regola di battitura

  • covariante se preserva l'ordinamento dei tipi (≤), che ordina tipi da più specifici a più generici;
  • contravariant se inverte questo ordine;
  • invariante o non variante se nessuna di queste condizioni si applica.

Covarianza e contraddizione

  • I tipi di dati (fonti) di sola lettura possono essere covarianti ;
  • i tipi di dati di sola scrittura (sink) possono essere contraddittori .
  • I tipi di dati mutabili che fungono sia da fonti che da pozzi dovrebbero essere invarianti .

Per illustrare questo fenomeno generale, considerare il tipo di array. Per il tipo Animale possiamo creare il tipo Animale []

  • covariante : un gatto [] è un animale [];
  • contravariante : un animale [] è un gatto [];
  • invariante : un animale [] non è un gatto [] e un gatto [] non è un animale [].

Esempi 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  

più esempi

jolly delimitato (ovvero diretto verso qualche parte) : esistono 3 diversi tipi di jolly:

  • In-varianza / Non-varianza: ?oppure ? extends Object- Senza limiti jolly . Rappresenta la famiglia di tutti i tipi. Usalo quando ottieni e metti entrambi.
  • Co-varianza: ? extends T(la famiglia di tutti i tipi che sono sottotipi di T) - un carattere jolly con un limite superiore . Tè la classe più in alto nella gerarchia dell'ereditarietà. Utilizzare un extendscarattere jolly solo quando si ottiene valori da una struttura.
  • Contro-varianza: ? super T(la famiglia di tutti i tipi che sono supertipi di T) - un carattere jolly con un limite inferiore . Tè la classe più bassa nella gerarchia dell'ereditarietà. Utilizzare un supercarattere jolly quando si inseriscono solo valori in una struttura.

Nota: il carattere jolly ?significa zero o una volta , rappresenta un tipo sconosciuto. Il carattere jolly può essere utilizzato come tipo di parametro, mai utilizzato come argomento di tipo per un richiamo di metodo generico, una creazione di istanza di classe generica (ovvero quando viene utilizzato un carattere jolly che non fa riferimento a nessun'altra parte del programma come noi T)

inserisci qui la descrizione dell'immagine

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 ed esempi


Ehi, volevo solo sapere cosa intendevi con l'ultima frase: "Se pensi che la mia analogia sia sbagliata, ti preghiamo di aggiornare". Vuoi dire se è eticamente sbagliato (che è soggettivo) o se è sbagliato nel contesto della programmazione (che è obiettivo: no, non è sbagliato)? Vorrei sostituirlo con un esempio più neutro che è universalmente accettabile indipendentemente dalle norme culturali e dalle credenze etiche; Se per te va bene.
Neurone,

alla fine sono riuscito a prenderlo. Bella spiegazione.
Oleg Kuts,

2
@Premraj, In-variance/Non-variance: ? or ? extends Object - Unbounded Wildcard. It stands for the family of all types. Use when you both get and put.Non riesco ad aggiungere elementi all'elenco <?> O all'elenco <? estende Object>, quindi non capisco perché possa essere Use when you both get and put.
LiuWenbin_NO.

1
@LiuWenbin_NO. - Quella parte della risposta è fuorviante. ?- il "jolly senza limiti" - corrisponde all'esatto contrario dell'invarianza. Fare riferimento alla seguente documentazione: docs.oracle.com/javase/tutorial/java/generics/… che indica: Nel caso in cui il codice debba accedere alla variabile come variabile "in" e "out", fare non utilizzare un carattere jolly. (Stanno usando "in" e "out" come sinonimo di "get" e "put"). Ad eccezione di nullte, non puoi aggiungerlo a una Collezione con parametri ?.
mouselabs

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

Quindi "? B si estende" dovrebbe essere interpretato come "? B si estende". È qualcosa che B estende in modo da includere tutte le super classi di B fino a Object, escluso B stesso. Grazie per il codice!
Saurabh Patil,

3
@SaurabhPatil No, ? extends Bsignifica B e qualsiasi cosa si estenda B.
chiede il

24

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

Ciò significa che quando un tipo con parametri che viene passato a un metodo produrrà istanze di T(saranno recuperate da esso in qualche modo), ? extends Tdovrebbe essere usato, poiché anche qualsiasi istanza di una sottoclasse di Tè a T.

Quando un tipo con parametri che viene passato a un metodo consumerà istanze di T(saranno passati ad esso per fare qualcosa), ? super Tdovrebbe essere usato perché un'istanza di Tpuò essere legalmente passata a qualsiasi metodo che accetta un supertipo di T. A Comparator<Number>potrebbe essere usato su un Collection<Integer>, per esempio. ? extends Tnon funzionerebbe, perché a Comparator<Integer>non poteva operare su a Collection<Number>.

Si noti che generalmente si dovrebbe usare solo ? extends Te ? super Tper i parametri di alcuni metodi. I metodi dovrebbero usare solo Tcome parametro type su un tipo restituito generico.


1
Questo principio vale solo per le collezioni? Ha senso quando si cerca di correlarlo con un elenco. Se si pensa alla firma dell'ordinamento (Elenco <T>, Comparatore <? Super T>) ---> qui il comparatore utilizza super, quindi significa che è un consumatore nel contesto PECS. Quando guardi l'implementazione per esempio come: public int compare (Persona a, Persona b) {return a.age <b.age? -1: a.age == b.age? 0: 1; } Sento che la persona non consuma nulla ma produce età. Questo mi rende confuso. C'è un difetto nel mio ragionamento o PECS vale solo per le collezioni?
Fatih Arslan,

24

In poche parole, tre semplici regole da ricordare PECS:

  1. Utilizzare il <? extends T>carattere jolly se è necessario recuperare un oggetto di tipo Tda una raccolta.
  2. Utilizzare il <? super T>carattere jolly se è necessario inserire oggetti di tipo Tin una raccolta.
  3. Se devi soddisfare entrambe le cose, beh, non usare alcun carattere jolly. Così semplice.

10

assumiamo 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

Chiariamo PE - Il produttore estende:

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

Perché non puoi aggiungere oggetti che estendono "Shark" in questo elenco? piace:

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

Dato che hai un elenco che può essere di tipo A, B o C in fase di esecuzione , non puoi aggiungere alcun oggetto di tipo A, B o C perché puoi finire con una combinazione non consentita in Java.
In pratica, il compilatore può effettivamente vedere a compiletime che aggiungi una B:

sharks.add(new HammerShark());

... ma non ha modo di sapere se in fase di esecuzione, la tua B sarà un sottotipo o un supertipo del tipo di elenco. In fase di esecuzione il tipo di elenco può essere uno dei tipi A, B, C. Quindi non è possibile aggiungere HammerSkark (super tipo) in un elenco, ad esempio, di DeadHammerShark.

* Dirai: "OK, ma perché non posso aggiungere HammerSkark perché è il tipo più piccolo?". Risposta: è il più piccolo che conosci. Ma HammerSkark può essere esteso anche da qualcun altro e si finisce nello stesso scenario.

Chiariamo CS - Consumer Super:

Nella stessa gerarchia possiamo provare questo:

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

Cosa e perché puoi aggiungere a questo elenco?

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

Puoi aggiungere i tipi di oggetti sopra indicati perché qualsiasi cosa sotto lo squalo (A, B, C) sarà sempre sottotipo di qualsiasi cosa sopra lo squalo (X, Y, Z). Facile da capire.

Non è possibile aggiungere tipi sopra Shark, poiché in fase di runtime il tipo di oggetto aggiunto può essere superiore nella gerarchia rispetto al tipo dichiarato dell'elenco (X, Y, Z). Questo non è permesso

Ma perché non puoi leggere da questo elenco? (Voglio dire che puoi estrarre un elemento da esso, ma non puoi assegnarlo ad altro che all'Oggetto o):

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

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

In fase di esecuzione, il tipo di elenco può essere di qualsiasi tipo sopra A: X, Y, Z, ... Il compilatore può compilare l'istruzione di assegnazione (che sembra corretta) ma, in fase di esecuzione, il tipo di s (Animale) può essere inferiore in gerarchia rispetto al tipo dichiarato dell'elenco (che potrebbe essere Creatura o superiore). Questo non è permesso

Per riassumere

Usiamo <? super T>per aggiungere oggetti di tipo uguale o inferiore Ta List. Non possiamo leggere da esso.
Usiamo <? extends T>per leggere oggetti di tipo uguale o inferiore Tdalla lista. Non possiamo aggiungere elementi ad esso.


9

(aggiungendo una risposta perché mai abbastanza esempi con i caratteri jolly di 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);
        }
    }

4

Questo è il modo più chiaro e più semplice per me pensare di estensioni contro super:

  • extendsè per la lettura

  • superè per scrivere

Trovo che "PECS" sia un modo non ovvio di pensare a cose riguardanti chi è il "produttore" e chi è il "consumatore". "PECS" è definito dal punto di vista della raccolta di dati stessa : la raccolta "consuma" se gli oggetti vengono scritti su di essa (sta consumando oggetti dal codice chiamante) e "produce" se gli oggetti vengono letti da essa (esso sta producendo oggetti con un codice chiamante). Questo è contrario a come tutto il resto è chiamato però. Le API Java standard sono denominate dal punto di vista del codice chiamante, non della raccolta stessa. Ad esempio, una vista incentrata sulla raccolta di java.util.List dovrebbe avere un metodo denominato "receive ()" anziché "add ()" - dopo tutto,riceve l'elemento.

Penso che sia più intuitivo, naturale e coerente pensare alle cose dal punto di vista del codice che interagisce con la raccolta - il codice "legge da" o "scrive" nella raccolta? Successivamente, qualsiasi codice che scrive nella collezione sarebbe il "produttore", e qualsiasi codice letto dalla collezione sarebbe il "consumatore".


Mi sono imbattuto in quella stessa collisione mentale e tenderei ad essere d'accordo, tranne per il fatto che PECS non specifica la denominazione del codice e che i limiti del tipo stessi sono impostati nelle dichiarazioni di raccolta. Inoltre, per quanto riguarda la denominazione, spesso si hanno nomi per la produzione / consumo di Collezioni come srce dst. Quindi hai a che fare sia con il codice che con i contenitori allo stesso tempo e ho finito per pensarci su queste linee - il "consumo di codice" consuma da un contenitore di produzione e "la produzione di codice" produce per un contenitore di consumo.
mouselabs,

4

La "regola" PECS garantisce solo che quanto segue sia legale:

  • Consumatore: qualunque cosa ?sia, può legalmente fare riferimento T
  • Produttore: qualunque cosa ?sia, può essere legalmente indicato da T

L'accoppiamento tipico lungo le linee di List<? extends T> producer, List<? super T> consumerassicura semplicemente che il compilatore possa applicare le regole standard di relazione ereditaria "IS-A". Se potessimo farlo legalmente, potrebbe essere più semplice dirlo <T extends ?>, <? extends T>(o meglio ancora a Scala, come puoi vedere sopra, è [-T], [+T]. Sfortunatamente il meglio che possiamo fare è <? super T>, <? extends T>.

Quando l'ho incontrato per la prima volta e l'ho rotto nella mia testa, la meccanica aveva un senso, ma il codice stesso continuava a sembrare confuso per me - continuavo a pensare "sembra che i limiti non debbano essere invertiti in quel modo" - anche se era chiaro quanto sopra - che si tratta semplicemente di garantire il rispetto delle regole standard di riferimento.

Ciò che mi ha aiutato è stato guardarlo usando il compito ordinario come analogia.

Considera il seguente codice giocattolo (non pronto per la produzione):

// copies the elements of 'producer' into 'consumer'
static <T> void copy(List<? extends T> producer, List<? super T> consumer) {
   for(T t : producer)
       consumer.add(t);
}

Illustrandolo in termini di analogia del compito, per consumeril ?carattere jolly (tipo sconosciuto) è il riferimento - il "lato sinistro" del compito - e <? super T>garantisce che qualunque cosa ?sia, T"IS-A" ?- che Tpuò essere assegnata a esso, perché ?è un super tipo (o al massimo lo stesso tipo) di T.

Poiché producerla preoccupazione è la stessa, è appena capovolta: produceril ?carattere jolly (tipo sconosciuto) è il referente - il "lato destro" del compito - e <? extends T>garantisce che qualunque cosa ?sia,? 'is-a' T- che si può essere assegnato a unT , poiché ?è un sottotipo (o almeno lo stesso tipo) di T.


2

Ricorda questo:

Il consumatore mangia la cena (super); Il produttore estende la fabbrica dei suoi genitori


1

Usando l'esempio della vita reale (con alcune semplificazioni):

  1. Immagina un treno merci con vagoni merci come analogia a un elenco.
  2. È possibile inserire un carico in un vagone merci se il carico ha dimensioni uguali o inferiori rispetto al vagone merci =<? super FreightCarSize>
  3. Puoi scaricare un carico da un vagone merci se hai abbastanza spazio (più della dimensione del carico) nel tuo deposito =<? extends DepotSize>

1

Covarianza : accettare i sottotipi
Contravarianza : accettare i supertipi

I tipi covarianti sono di sola lettura, mentre i tipi contraddittori sono di sola scrittura.


0

Diamo un'occhiata ad esempio

public class A { }
//B is A
public class B extends A { }
//C is A
public class C extends A { }

Generics ti consente di lavorare con i tipi in modo dinamico in modo sicuro

//ListA
List<A> listA = new ArrayList<A>();

//add
listA.add(new A());
listA.add(new B());
listA.add(new C());

//get
A a0 = listA.get(0);
A a1 = listA.get(1);
A a2 = listA.get(2);
//ListB
List<B> listB = new ArrayList<B>();

//add
listB.add(new B());

//get
B b0 = listB.get(0);

Problema

Poiché Java's Collection è un tipo di riferimento, di conseguenza abbiamo i seguenti problemi:

Problema n. 1

//not compiled
//danger of **adding** non-B objects using listA reference
listA = listB;

* Il generico di Swift non ha questo problema perché Collection è Value type[About], quindi viene creata una nuova collezione

Problema n. 2

//not compiled
//danger of **getting** non-B objects using listB reference
listB = listA;

La soluzione: caratteri jolly generici

Il carattere jolly è una funzione del tipo di riferimento e non può essere istanziato direttamente

Soluzione n. 1 <? super A> aka limite inferiore noto anche come contraddizione aka consumatori garantisce che è gestito da A e da tutte le superclassi, ecco perché è sicuro aggiungere

List<? super A> listSuperA;
listSuperA = listA;
listSuperA = new ArrayList<Object>();

//add
listSuperA.add(new A());
listSuperA.add(new B());

//get
Object o0 = listSuperA.get(0);

Soluzione n. 2

<? extends A>aka limite superiore aka covarianza aka produttori garantisce che è gestito da A e tutte le sottoclassi, ecco perché è sicuro ottenere e trasmettere

List<? extends A> listExtendsA;
listExtendsA = listA;
listExtendsA = listB;

//get
A a0 = listExtendsA.get(0);

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.