Problema con Generics e Wildcards

di il
4 risposte

Problema con Generics e Wildcards

Salve, ho questa consegna:
creare una biblioteca che deve avere una lista di cataloghi. Ogni catalogo può contenere o solo riviste o solo giornali. Rivista e Giornale sono figlie della super classe astratta Libro.
Io ho operato così:

Creo la classe astratta Libro e le due figlie Rivista e Giornale
public class Libro {}
public class Giornale extends Libro {}
public class Rivista extends Libro {}
Creo poi la classe Catalogo e inserisco il generics <T extends Libro> così che quando andrò a creare una istanza di Catalogo potrò scegliere cosa mettere al suo interno tra Rivista e Giornale dato che entrambe ereditano Libro.
public class Catalogo <T extends Libro> {
	
	ArrayList<T> libri = new ArrayList<T>();
	
	public void addLibro(T newLibro) {
		libri.add(newLibro);
	}

}
Ora creo la classe Biblioteca. Ha un ArrayList di cataloghi qualsiasi, nel senso che al suo interno possono esserci sia Catalogo<Rivista> che Catalogo<Giornale> e due metodi: il primo che aggiunge un catalogo alla biblioteca e uno che, dati un catalogo e un Rivista/Giornale, aggiunge a quel catalogo la Rivista/Giornale passato.
public class Biblioteca {
	
	ArrayList<Catalogo<? extends Libro>> cataloghi = new ArrayList<Catalogo<? extends Libro>>();
	
	public void addCatalogo(Catalogo<? extends Libro> newCatalogo) {
		cataloghi.add(newCatalogo);
	}
	
	public void addLibro(Catalogo<? extends Libro> catalogo, Libro newLibro) {
		cataloghi.get(cataloghi.indexOf(catalogo)).addLibro(newLibro);
	}

}
Ho però un problema sul secondo metodo:
The method addLibro(capture#3-of ? extends Libro) in the type Catalogo<capture#3-of ? extends Libro> is not applicable for the arguments (Libro)

Come posso risolvere? Ovvero come posso creare un metodo in Catalogo che aggiunge al suo interno un qualsiasi tipo di classe che estende Libro?

Grazie in anticipo

4 Risposte

  • Re: Problema con Generics e Wildcards

    Il problema risulta più comprensibile se pensi a cosa significhi "T extends Libro", ovvero a quali tipologie di oggetto possono essere contenute dentro a un Catalogo. Dividiamo il problema in 2: cosa puoi estrarre da un catalogo e cosa puoi inserire in un catalogo:

    Estrazione:
    - Puoi estrarre un Object? Si, perché ogni oggetto deriva da Object, per cui qualunque sottoclasse di Libro è anche sottoclasse di Object
    - Puoi estrarre un Integer? No, perché Integer non è sottoclasse di Libro
    - Puoi estrarre un Libro? Si, perché sai che Catalogo può solo contenere sottoclassi di Libro, e Libro è una sottoclasse di Libro
    - Puoi estrarre una Rivista? No, perché Catalogo può contenere qualunque tipo di Libro, ad esempio un Giornale

    Inserimento:
    - Puoi inserire un Object? No, perché Catalogo deve contenere una sottoclasse di Libro
    - Puoi inserire un Libro? No, perché non puoi sapere quale specifica sottoclasse di Libro sarà contenuta dentro a Catalogo a runtime (es: magari è un catalogo di Giornali)
    - Puoi inserire un Giornale? No, perché magari è un catalogo di Riviste

    Risultato: da una collezione del tipo "T extends Libro" puoi solamente estrarre oggetti ed essere sicuro che essi siano libri, ma non puoi aggiungere nulla perché non puoi sapere quale sia il tipo effettivo a runtime.

    Per saperne di più:
  • Re: Problema con Generics e Wildcards

    little_lakes ha scritto:


    Creo la classe astratta Libro e le due figlie Rivista e Giornale
    public class Libro {}
    public class Giornale extends Libro {}
    public class Rivista extends Libro {}
    Ma non avevi detto Libro astratto?? Comunque fin qui, di per sé, ok.

    little_lakes ha scritto:


    Creo poi la classe Catalogo e inserisco il generics <T extends Libro> così che quando andrò a creare una istanza di Catalogo potrò scegliere cosa mettere al suo interno tra Rivista e Giornale dato che entrambe ereditano Libro.
    Attenzione a una cosa: il bound extends Libro permette solo di limitare una parametrizzazione concreta a qualcosa che è o estende Libro.

    Quindi si può fare:

    Catalogo<Libro> catLibri = new Catalogo<Libro>();
    oppure
    Catalogo<Giornale> catGiornali = new Catalogo<Giornale>();
    oppure
    Catalogo<Rivista> catRiviste = new Catalogo<Rivista>();

    ma NON ad esempio un Catalogo<String> o un Catalogo<Integer> ecc...

    Però, attenzione, Catalogo<Libro> , Catalogo<Giornale> e Catalogo<Rivista> sono 3 tipi differenti e NON in relazione tra di loro. In particolare un Catalogo<Giornale> NON è un sottotipo di Catalogo<Libro> (nonostante Giornale sia un sottotipo di Libro).

    E in un Catalogo<Libro> ci puoi mettere sia oggetti Giornale che Rivista, chiaramente.

    little_lakes ha scritto:


    Ora creo la classe Biblioteca. Ha un ArrayList di cataloghi qualsiasi, nel senso che al suo interno possono esserci sia Catalogo<Rivista> che Catalogo<Giornale>
    Usare ArrayList<Catalogo<? extends Libro>> è di per sé corretto per il fine descritto.
    Cioè, in un ArrayList del genere puoi in effetti metterci sia un Catalogo<Giornale> che un Catalogo<Rivista> (come anche un Catalogo<Libro>).

    Il problema semmai è nella estrazione. Immagina che il ArrayList contenga 2 elementi, un Catalogo<Giornale> e un Catalogo<Rivista>.
    Tu puoi "vedere" un elemento solo come Catalogo<? extends Libro>

    Catalogo<? extends Libro> primoCat = cataloghi.get(0);

    E il problema è che in Catalogo<? extends Libro> NON ci puoi inserire nulla! (a parte un null letterale). Puoi solo estrarre un oggetto come Libro (o al limite super-tipo).

    little_lakes ha scritto:


    Ho però un problema sul secondo metodo:
    The method addLibro(capture#3-of ? extends Libro) in the type Catalogo<capture#3-of ? extends Libro> is not applicable for the arguments (Libro)

    Come posso risolvere?
    Non risolvi ... non così.

    Tu vorresti inserire un oggetto che è-un Libro quindi materialmente l'oggetto potrebbe essere un Giornale o un Rivista (non esattamente Libro perché abstract come volevi). E vorresti metterlo in un Catalogo<? extends Libro> che non sai che cosa è. Non sai se concretamente era (quando l'hai inserito) un Catalogo<Giornale> o un Catalogo<Rivista>.
    Se fosse un Catalogo<Rivista> e tu volessi metterci dentro un Giornale? Ovviamente NOOO. Tutta la teoria dei generics andrebbe a farsi friggere .... e infatti il compilatore te lo proibisce!

    Come ho già detto prima, in un Catalogo<? extends Libro> non si può inserire nulla (a parte null letterale).

    Il problema di "fondo" (dovuto alla erasure dei generics) è che quando inserisci un es. Catalogo<Giornale> all'interno del ArrayList<Catalogo<? extends Libro>>, quella parametrizzazione concreta <Giornale> in pratica "si perde". Non c'è più scritto da nessuna parte nel ArrayList che l'elemento a indice es. 2 è un Catalogo<Giornale> !!!
  • Re: Problema con Generics e Wildcards

    Perfetto. Vi ringrazio, siete stati chiarissimi.
    Una soluzione quindi potrebbe essere creare 2 ArrayList separate, una ArrayList<Catalogo<Giornale>> per tutti i cataloghi di giornali e una ArrayList<Catalogo<Rivista>> per tutti i cataloghi rivista? Così non dovrei avere problemi nell'inserimento e nel prelievo di informazioni con le singole ArrayList. Confermate?
  • Re: Problema con Generics e Wildcards

    little_lakes ha scritto:


    Una soluzione quindi potrebbe essere creare 2 ArrayList separate, una ArrayList<Catalogo<Giornale>> per tutti i cataloghi di giornali e una ArrayList<Catalogo<Rivista>> per tutti i cataloghi rivista? Così non dovrei avere problemi nell'inserimento e nel prelievo di informazioni con le singole ArrayList. Confermate?
    Tecnicamente sì ma sarebbe una duplicazione (che fai se un giorno ci fosse anche un Dizionario ?).

    E dovresti pure duplicare i metodi. E con nomi distinti. Ad esempio NON puoi fare:

    public void addCatalogo(Catalogo<Giornale> cat) { ..... }
    public void addCatalogo(Catalogo<Rivista> cat) { ..... }

    Perché ... i due metodi hanno la stessa erasure. Dovresti fare addCatalogoGiornali e addCatalogoRiviste.
Devi accedere o registrarti per scrivere nel forum
4 risposte