Wildcard<? super T>... HELP!

di il
15 risposte

Wildcard<? super T>... HELP!

Ciao a tutti ragazzi, sto uscendo pazzo con queste dannate wildcards...

Allora, ho cercato ovunque e ho capito questo:

scrivere ad esempio LinkedList<? extends T> ls= new LinkedList<>();

significa creare una list che conterrà T o SOLO SUE SOTTOCLASSI, giusto? bene..

Scrivere inece LinkedList<? super T> ls= new LinkedList<>();

leggendo online leggo sempre che significa creare una list che conterrà istanze di T o sue superclassi, ma non è vero!
import java.util.LinkedList;



class classA{
	
	public String toString(){ return "Classe A";}
}
class classB extends classA{
	
	public String toString(){ return "Classe B extends classA";}
}
class classC extends classB{
	
	public String toString(){ return "Classe C extends classB";}
}

class classD extends classB{
	
	public String toString(){ return "Classe D extends classB";}
}

public class Wildcards {

	public static void main(String[] args){
		
		LinkedList<? super classB> Ls= new LinkedList<>();
		
		Ls.add(new Object());
		Ls.add(new classB());
		Ls.add(new classC());
		Ls.add(new classD());

		System.out.println(Ls);
	}
}
E poi un altro dubbio:

Se scrivo:
LinkedList<classB> Ls= new LinkedList<>();
significa che sto creando una list di elementi SICURAMENTE di classB, ma posso comunque aggiungere sottoclassi di classB giusto? vale il polimorfismo per inclusione no?

Scusate se magari porgo quesiti sciocchi, vi ringrazio comunque anticipatamente per l'aiuto che mi fornirete...

15 Risposte

  • Re: Wildcard<? super T>... HELP!

    Non avrei tempo purtroppo adesso di fare una nuova spiegazione esauriente. Quindi ti rimando a quanto dicevo su un altro forum: http://forum.html.it/forum/showthread.php?threadid=2926752
  • Re: Wildcard<? super T>... HELP!

    Ciao andbin, grazie di nuovo per il tuo aiuto ...

    Nel post hai scritto:
    Ora vediamolo con un metodo:

    public static void prova(List<? super Number> lan) { ........ }

    Se io lo invocassi con
    List<Object> lo; [...]
    prova(lo);

    Tu potresti pensare che all'interno del metodo si possa fare:

    lan.add(new Object());

    perché hai passato un List<Object>. E invece no. Perché vedendo la lista come <? super Number> la parametrizzazione concreta ripeto che non la sai (e nemmeno il compilatore la sa).
    L'unica cosa certa è quel limite inferiore. Qualunque cosa puoi lecitamente passare a 'prova', sicuramente è in grado di contenere un Double. Ma un Object no.
    Non ho capito bene cosa intendi dire quando dici "ma un object no".

    Praticamente io devo inserire in questa lista (lan) tutto ciò che può essere contenuto da number e le sue superclassi, giusto? Ma questo significa che se object è superclasse di number, automaticamente può contenere cose più "grosse" (superclassi) di number, giusto?

    Mi sto perdendo qualcosa :/
  • Re: Wildcard<? super T>... HELP!

    Il wildcard ? non riguarda il contenuto dell'oggetto (es. gli elementi in una lista). Detto in altro modo, ad esempio List<?> NON vuol dire di per sé che la lista può contenere qualunque cosa.

    Invece vuol dire che semplicemente non "sai" quale è la parametrizzazione concreta che ti viene passata/assegnata.

    Prendiamo l'esempio che avevo già fatto:

    public static void prova(List<? super Number> lista) { ........ }

    Potrei avere le seguenti variabili:

    List<Object> lo = new ArrayList<Object>();
    List<Number> ln = new ArrayList<Number>();
    List<Serializable> lser = new ArrayList<Serializable>();
    List<String> ls = new ArrayList<String>();
    List<Date> ld = new ArrayList<Date>();

    Il metodo prova posso invocarlo con:

    prova(lo);
    prova(ln);
    prova(lser); // nota: Number implementa Serializable

    Mentre invece NON posso invocarlo con:

    prova(ls);
    prova(ld);

    Questo perché String e Date non hanno alcuna relazione con Number (non sono super-tipi di Number) e quindi il bound non regge in questi casi e la invocazione pertanto viene rifiutata dal compilatore.

    Ora, il List<? super Number> del metodo vuol appunto dire che non sai quale è parametrizzazione concreta che ti viene passata (<Object>, <Number> o <Serializable>). Sai solo che come minimo è uguale o più ampia di Number.

    Con questa nozione/dubbio, quindi nel metodo cosa puoi inserire nella lista? Puoi inserire solo oggetti che derivano da Number, es. Double, Integer, Long .... Questo perché qualunque sia la parametrizzazione concreta che viene passata, è sicuramente in grado di contenere questi oggetti.

    Nel metodo non puoi fare:

    lista.add(new Object());
    oppure
    lista.add("prova");

    Se io passassi un List<Object>, questo è perfettamente in grado di contenere Object e String. Ma il punto è quello che ho detto prima. Nel metodo NON sai quale è la parametrizzazione e pertanto deve essere proibita l'aggiunta di qualcosa di cui non sei certo che possa essere davvero contenuto. Ed è proprio questo il senso del wildcard con/senza bound. Offrire una sorta di polimorfismo permettendo di accettare più tipi di parametrizzazioni ma chiaramente ponendo dei vincoli per evitare che la teoria (dei generics) vada a farsi friggere.

    Per finire, un esempio più "materiale": poniamo che tu devi darmi un oggetto che io poi metto in una scatola. Io non ti dico le dimensioni esatte della scatola che ho, ti dico solo che come minimo è grande 10x10x10 cm. Tu solo con questa nozione sei certo che puoi tranquillamente darmi un oggetto più piccolo perché sicuramente può essere contenuto.
    Mentre invece non devi darmi un oggetto più grande perché non saresti certo se è possibile metterlo nella scatola oppure no.

    Il concetto è esattamente proprio questo.
  • Re: Wildcard<? super T>... HELP!

    Andrea sei stato magnifico, grazie davvero... mi dispiace averti creato perdite di tempo...

    ora ho capito perfettamente cosa significa <? super T>.

    l'unica cosa che ora non mi è chiara è:

    perchè usare la ? super invece della ? extends?

    in entrambi i casi posso inserire solo ed esclusivamente istanze della classe T (? super T/? extends T)... solo che con la extends non posso scrivere, perchè non so che istanze effettivamente siano presenti nella lista. invece con la super posso scrivere perchè sono certo che quelle operazioni sono contenute nel "dominio" della super, poichè garantisce sicuramente la presenza di istanze da number in giù...

    E' questa la risposta definitiva?

    Ti ringrazio infinitamente...
  • Re: Wildcard<? super T>... HELP!

    Francesco93 ha scritto:


    Andrea sei stato magnifico, grazie davvero... mi dispiace averti creato perdite di tempo...
    Prego, nessun problema. Son qui apposta.

    Francesco93 ha scritto:


    perchè usare la ? super invece della ? extends?
    <? extends Qualcosa> è sostanzialmente l'opposto del <? super Qualcosa>, mentre con super hai un limite inferiore, con extends hai un limite superiore.

    Riprendendo l'esempio delle scatole, se ti dico che ho una scatola di dimensioni massime 50x50x50 cm (ma potrebbe essere più piccola ... molto più piccola), tu sapendo solo questo cosa puoi inserire nella mia scatola? Assolutamente nulla!

    In un List<? extends Number> NON puoi inserire nulla, né Integer, né Long, né Object né altro.
    L'unica cosa che tecnicamente è possibile aggiungere è un null "letterale".
    List<? extends Number> lista = .........
    lista.add(null);
    Perché null è concettualmente il sotto-tipo di tutti i tipi reference. È un po' come se dicessi che null è un oggetto materiale talmente piccolo, infinitesimamente piccolo che quindi può stare in qualunque "scatola".

    La dualità super/extends nei generics viene descritta con quello che si dice il "Get and Put principle". Ovvero <? super Qualcosa> si usa solo per inserire, mentre <? extends Qualcosa> si usa solo per estrarre.
  • Re: Wildcard<? super T>... HELP!

    Posso fare ancora un esempio. Supponiamo di voler fare un metodo che sposta gli elementi da una collezione ad un'altra. Per "spostare" qui intendo nel senso di toglierli proprio (remove) dalla collezione A e inserirli in una collezione B.

    Il metodo basilare/banale potrebbe essere:
    public static <T> void spostaElementi(Collection<T> sorgente, Collection<T> destinazione) {
        Iterator<T> iter = sorgente.iterator();
        while (iter.hasNext()) {
            T elem = iter.next();
            destinazione.add(elem);
            iter.remove();
        }
    }
    Peccato solo che così è parecchio limitato. Se ho un List<Number> posso passare gli elementi in un List o Set ma sempre e comunque di <Number> (il T è sempre lo stesso per sorgente e destinazione).
    Però se ci pensi, qualunque cosa ci sia in un Collection<Number> può anche stare benissimo in un Collection<Object>. Ma con la firma del metodo sopra non è lecito questo passaggio.

    Ecco che vengono in aiuto extends/super. Si può quindi fare:
    public static <T> void spostaElementi(Collection<? extends T> sorgente, Collection<? super T> destinazione) {
        Iterator<? extends T> iter = sorgente.iterator();
        while (iter.hasNext()) {
            T elem = iter.next();
            destinazione.add(elem);
            iter.remove();
        }
    }
    Ora posso passare es.:
    - sorgente List<Number> e destinazione Set<Object>
    - sorgente Set<GregorianCalendar> e destinazione Collection<Calendar>
    ecc...
  • Re: Wildcard<? super T>... HELP!

    Ora è tutto fin troppo chiaro grazie!

    ho solo una domanda.

    Ammettiamo di avere una classe Elemento che implementa comparable<? extends Elemento>
    una classe Collezione<T> che contiene una collection (<T>) e dei metodi che la popolano e la stampano.
    in teoria la collezione dovrebbe essere istanziata: Collezione<Elemento> miaCollezione= new miaCollezione<>();

    ho un esercizio che dice: implementare la soluzione richiesta (una serie di passaggi quali popolamento caricamento e salvataggio) sia usando le java generics che non usandole.

    ora, nel mio main io non uso le generics se non creo la mia collezione con il parametro, giusto?

    bene, ora però mi domandavo...

    potrei usare le wildcard nella mia classe Elemento per generare errori in fase di compilazione e non a runtime?

    ma se per esempio nel mio main io non specifico i parametri, quindi implicitamente starò creando un'istanza di Collezione<Object>, giusto?

    e quando il metodo compareTo(?extends Elemento) viene chiamato, se per esempio sto lavorando con una Collezione senza parametro, il compilatore può segnalarmelo? perchè per ora non riesco ad impedire la classcastexception, è giusto che ci sia e la comprendo, ma pensavo che usando le generics e le wildcard sulla comparable (<?extends Elemento) avessi potuto generare errori al compile time e costringere il programmatore a correggere la propria implementazione nel main..

    E' fattibile o sto dicendo baggianate?

    Grazie ancora e scusa l'insistenza
  • Re: Wildcard<? super T>... HELP!

    Purtroppo non mi è molto chiaro questo tuo ultimo discorso .... dovrei sicuramente rileggerlo meglio.
    Anche perché dici:

    che implementa comparable<? extends Elemento>

    Un Comparable<? extends Elemento> non ha senso e non può funzionare. Se hai capito cosa ho detto prima, allora hai compreso che se hai una parametrizzazione con un bounded wildcard e devi invocare un metodo che riceve il tipo del bound, allora deve avere ? super .... NON ? extends.
  • Re: Wildcard<? super T>... HELP!

    Si ma io nella compareTo in teoria dovrei solo leggere l'oggetto che ricevo, giusto? percui non ho usato la super..
    comunque vedo di postare un esempio appena riprenderò il codice sotto mano

    buon pranzo comunque
  • Re: Wildcard<? super T>... HELP!

    Francesco93 ha scritto:


    si ma io nella compareTo in teoria dovrei solo leggere l'oggetto che ricevo, giusto? percui non ho usato la super..
    No, sbagliato. compareTo(T o) infatti ha un argomento, è questo che conta nei confronti del bound!
  • Re: Wildcard<? super T>... HELP!

    Poniamo di voler fare un metodo che ritorna l'elemento "max" di una lista, basandosi su Comparable. Quindi l'elemento maggiore nel senso proprio dell'ordinamento dato da Comparable.

    Questa è una forma (che è corretta e compila):
    public static <T extends Comparable<? super T>> T max(List<T> lista) {
        Iterator<T> iter = lista.iterator();
        T max = iter.next();
        
        while (iter.hasNext()) {
            T elem = iter.next();
            if (elem.compareTo(max) > 0) {
                max = elem;
            }
        }
        
        return max;
    }
    Nota: in java.util.Collections c'è già un max(), è anche più "ampio" e generico del mio. Il suo codice dovrebbe essere praticamente quasi uguale al mio (la logica è quella ...).

    Il punto è che se metti

    public static <T extends Comparable<? extends T>> T max(List<T> lista) {

    semplicemente NON ti compila più. Perché con extends non puoi passare più nulla in argomento al compareTo !

    Ripeto il Get and Put principle: ? extends serve per estrarre (ricevere da un metodo) mentre ? super serve per inserire (passare ad un metodo).
  • Re: Wildcard<? super T>... HELP!

    Provando ad esercitarmi, voglio creare una classe elemento generico che sfrutti le wildcards.
    ti mostro il mio ragionamento:

    la mia classe contiene un solo oggetto di tipo T, di nome myValue che deve implementare Comparable, percui la mia classe è formata da: T extends Comparable.

    qui uso extends perchè non mi interessa di cosa sia istanza, ma così sicuramente implementerà la Comparable.

    Ora la comparable sfrutta le generics, pertanto la myValue deve implementare Comparable<>.

    Cosa inserirò in questa comparable? da ciò che mi hai detto. la myValue userà il metodo: compareTo, che userò per ottenere informazioni sull'oggetto in input.

    ora: questa potrebbe essere la compareTo di MYVALUE (non di ELEMENT!):
    public void int compareTo(? super T o){
    
    }
    sappiamo che myValue è un'istanza di una classe che sicuramente implementerà comparable<?super T>, perchè T in Element è vincolato dall'extends...

    ora, abbiamo detto che ci va ? super t perchè

    se fosse extends: sappiamo che può essere qualunque istanza di sottoclassi di Comparable, e io che devo confrontare il mio valore non posso essere certo che tra tutte le infinite classi che implementano comparable, l'oggetto passato in input sia esattamente del tipo di myValue implements Comparable, è così?

    quindi usiamo la super, e rifacendomi all'esempio della scatola che mi facesti so che:
    non so nulla dell'oggetto ricevuto, ma so che COME MINIMO, è istanza della classe a cui appartiene myValue, pertanto posso ottenerne gli attributi perchè sicuramente ci sono. E' così?

    Ora... passiamo ad Element... anche questa classe deve implementare Comparable, come abbiamo detto prima... percui scriveremo:

    Element implements Comparable<Element<...

    Element di chi? Beh ma di T, no? in questo modo legheremo l'interfaccia Comparable esattamente al tipo Element<T extends Comparable<? superT>>.

    ora però, ho notato che funziona ugualmente anche per:
    public class Element<T extends Comparable<? super T>> implements Comparable<Element<? extends T>>{
    
    	public int compareTo(Element<? extends T> o) {
    
    		return (myValue.compareTo(o.myValue));
    		
    	}
    }


    Spero che il mio ragionamento sia corretto, in tal caso, potresti aiutarmi a capire il perchè di quest'ultimo punto? grazie mille, davvero
    package container_2.container;
    
    public class Element<T extends Comparable<? super T>> implements Comparable<Element<T>>{
    
    	T myValue;
    	
    	public Element(T value) {
    
    		myValue=value;
    	}
    
    
    	@Override
    	public int compareTo(? super Element<T> o) {
    
    		return myValue.compareTo(o.myValue);
    	}
    }
    
  • Re: Wildcard<? super T>... HELP!

    Guarda .. sinceramente non riesco più a "seguirti", nel senso che ho letto il tuo ultimo post ma hai detto talmente tante cose che probabilmente sono io che non ho compreso bene i tuoi dubbi.

    Quindi cerco adesso di spiegarti come fare una classe del genere, così vediamo se ho capito e magari comprendi meglio anche tu. Procederemo per gradi, così vedi anche i ragionamenti che faccio.


    -- PASSO 1 --

    Iniziamo con il fare una classe "generica" che contiene un solo valore tipizzato con la type variable, cioè appunto della parametrizzazione generica.
    La cosa più banale/basilare è questa:
    public class Contenitore<T> {
        private T valore;
    
        // ... costruttore/i, metodi pubblici getter/setter per il valore, ecc...
    }
    Non credo ci sia molto da dire qui. Si può istanziare una parametrizzazione concreta di qualunque tipo, posso fare un Contenitore<Object>, Contenitore<String>, Contenitore<Image> ecc...
    Non ci sono wildcard né vincoli (i bound) quindi qualunque tipo è lecito.


    -- PASSO 2 --

    Ora voglio restringere la istanziazione di una parametrizzazione concreta ai soli tipi che sono Comparable. Cioè il valore contenuto deve implementare Comparable.
    La cosa si risolve semplicemente aggiungendo un bound alla type variabile della classe.
    public class Contenitore<T extends Comparable<? super T>> {
        private T valore;
    
        // ... costruttore/i, metodi pubblici getter/setter per il valore, ecc...
    }
    Il perché ho messo ? super T l'ho già spiegato prima. Si può giustificare anche in un altro modo, facendo l'esempio di una ipotetica gerarchia di classi, come quella classica: Frutto poi sotto-classi Mela e Arancia.

    Supponiamo di avere:
    class Frutto implements Comparable<Frutto> { ... }
    class Mela extends Frutto { ... }
    class Arancia extends Frutto { ... }
    La implementazione di Comparable è in Frutto (magari compara es. sul peso che ha senso averlo in Frutto). Nelle sotto-classi la parametrizzazione di Comparable non si può cambiare. Quindi Mela è Comparable<Frutto> .... NON Comparable<Mela>

    Se avessi fatto Contenitore come

    public class Contenitore<T extends Comparable<T>>

    Allora NON avrei potuto fare un Contenitore<Mela> perché si aspetterebbe che Mela sia Comparable<Mela> ma non è così.

    Mentre con ? super la forma diventa più ampia e permette un Contenitore<Mela> anche quando Mela è Comparable<Frutto>.


    In questo momento quindi la parametrizzazione concreta di Contenitore è più limitata. Posso fare un Contenitore<String>, Contenitore<Integer> ma NON posso fare es. Contenitore<Object> (Object non è Comparable) né ad esempio Contenitore<Image> (intendo java.awt.Image che idem non è Comparable).


    -- PASSO 3 --

    Nel passo 2 ho ragionato solo sulla comparabilità del valore contenuto in Contenitore. Ora ragioniamo sulla comparabilità proprio degli oggetti Contenitore tra di loro.

    Quando si fa una classe es. Persona "comparabile", generalmente la si fa così:
    public class Persona implements Comparable<Persona> {
        private String nome;
        ......
    }
    Qui come vedi non ci sono wildcard o bound. Generalmente quello che interessa è che un oggetto Persona sia comparabile SOLO con altri oggetti Persona. Non con altro (che avrebbe poco/nessun senso).

    La classe Contenitore però è più complessa della classe Persona, perché Contenitore è una classe "generica" ed ha una type variable. Quindi semplicemente bisogna far rispecchiare questo anche nel implements del Comparable per Contenitore.
    public class Contenitore<T extends Comparable<? super T>> implements Comparable<Contenitore<T>> {
        private T valore;
    
        // ... costruttore/i, metodi pubblici getter/setter per il valore, ecc...
    
        public int compareTo(Contenitore<T> altroContenitore) {
            return getValore().compareTo(altroContenitore.getValore());
        }
    }
    (il getValore l'ho omesso per brevità ma è banale farlo e ci deve essere ovviamente)

    A questo punto Contenitore: a) accetta e contiene solo valori comparabili e b) gli oggetti Contenitore sono comparabili tra di loro, chiaramente solo con la stessa parametrizzazione, es. un Contenitore<String> è comparabile solo con un altro Contenitore<String>.


    Ho chiarito i tuoi dubbi?
  • Re: Wildcard<? super T>... HELP!

    Perfetto, la tua conclusione è identica alla mia

    io poi ho voluto creare element<? extends T> per vedere cosa succedeva e ho notato che il compilatore non dava problemi... questo perche io posso comparare un element con tutto ciò che è un element o che da esso è ereditato... quindi è praticamente LA STESSA COSA SCRIVERE:
    public class Element<T extends Comparable<? super T>> implements Comparable<Element<? extends T>>
    e:
    public class Element<T extends Comparable<? super T>> implements Comparable<Element<T>>
    giusto?
    public class Element<T extends Comparable<? super T>> implements Comparable<Element<? extends T>>{
    
       public int compareTo(Element<? extends T> o) {
    
          return (myValue.compareTo(o.myValue));
          
       }
    }
    sono felicissimo di aver capito le wildcards, grazie mille!!!
Devi accedere o registrarti per scrivere nel forum
15 risposte