[Java] tipi generici con esempio pratico

di il
8 risposte

[Java] tipi generici con esempio pratico

Ciao.
mi sto imbattendo nei tipi generici, ma mi sfugge il loro utilizzo, quando provo a creare un programmino java va sempre in errore in compilazione.
Chi è cosi gentile da aiutarmi con un esempio pratico? in particolare, vorrei creare un inventario di oggetti diversi.
Per didattica ho creato una classe astratta Prodotto con un solo parametro marca
poi classi Monitor, Stampante e Notebook che estendono Prodotto.
Alla fine vorrei creare una List<?> di ArrayList() e includere i tre oggetti diversi.

Mi sfugge come parametrizzare le classi, come accedere ai loro attributi e come stampare il contenuto dell'inventario.
Non riesco neanche ad aggiungere gli oggetti.

Questo quello che ho fatto

public abstract class Prodotto {
    private String marca;
    public String getMarca() {
        return marca;
    }
    public void setMarca(String marca) {
        this.marca = marca;
    }
}

public class Monitor extends Prodotto {
    private String tipo; //LCD o LED
    private int dimensioniPollici;
    public String getTipo() {
        return tipo;
    }
    public void setTipo(String tipo) {
        this.tipo = tipo;
    }
    public int getDimensioniPollici() {
        return dimensioniPollici;
    }
    public void setDimensioniPollici(int dimensioniPollici) {
        this.dimensioniPollici = dimensioniPollici;
    }    
}

public class Notebook extends Prodotto {
    private int ram; //GByte
    private String cpu;
    public int getRam() {
        return ram;
    }
    public void setRam(int ram) {
        this.ram = ram;
    }
    public String getCpu() {
        return cpu;
    }
    public void setCpu(String cpu) {
        this.cpu = cpu;
    }
}

public class Stampante extends Prodotto {
    private String tipo; //Laser, InkJet
    public String getTipo() {
        return tipo;
    }
    public void setTipo(String tipo) {
        this.tipo = tipo;
    }
}


import java.util.ArrayList;
public class Inventario {
    Monitor m = new Monitor();
    Stampante s = new Stampante();
    Notebook n = new Notebook();
    
    List<?> l = new ArrayList<>(); // NON FUNZIONA !!!!!
    
    ArrayList<Monitor> list1 = new ArrayList<>(); //OK
    ArrayList<Stampante> list2 = new ArrayList<>(); //OK
    ArrayList<Notebook> list3 = new ArrayList<>(); //OK
    ArrayList<?> list4 = new ArrayList<>(); //OK
    
    public void aggiungiOggetto() {
        list1.add(m);  //OK
        list2.add(s);  //OK
        list3.add(n);  //OK

        list4.add(s); //The method add(capture#1-of ?) in the type ArrayList<capture#1-of ?> is not applicable for the arguments (Stampante)
    }
}
Non capisco perché List<?> l = new ArrayList<>() non funziona, restituisce
List cannot be resolved to a type
Cannot infer type arguments for ArrayList<>

e comunque non riesco ad aggiungere oggetti diversi alla lista. Dovrei parametrizzare anche le classi?
Mi serve un esempio pratico per capire, il libro spiega in via teorica senza esempi e solo con list<String>.

Grazie mille
tagan

8 Risposte

  • Re: [Java] tipi generici con esempio pratico

    tagan ha scritto:


    Non capisco perché List<?> l = new ArrayList<>() non funziona, restituisce
    List cannot be resolved to a type
    Cannot infer type arguments for ArrayList<>
    List innanzitutto lo devi importare:
    import java.util.List;

    tagan ha scritto:


    e comunque non riesco ad aggiungere oggetti diversi alla lista.
    In un List<?> non puoi inserire nulla, con l'unica eccezione di un null letterale.
    Ho un articolo sul mio blog che lo spiega:
  • Re: [Java] tipi generici con esempio pratico

    Perfetto.
    capito tutto.
    il problema del List l'ho risolto con java.util.*; così taglio l atesta al toro.

    Poi mancava una classe generica creata come ProdottoPerLista <T> con il suo parametro T obj
    e poi la lista l'ho definita come List<ProdottoPerLista>. in questo modo mi creo dentro la classe generica ogni tipo di oggetto e la aggiungo alla lista.
    Era questo che mi sfuggiva.

    Grazie andbin.

    Ora funziona tutto.
    Tagan
  • Re: [Java] tipi generici con esempio pratico

    tagan ha scritto:


    Poi mancava una classe generica creata come ProdottoPerLista <T> con il suo parametro T obj
    e poi la lista l'ho definita come List<ProdottoPerLista>. in questo modo mi creo dentro la classe generica ogni tipo di oggetto e la aggiungo alla lista.
    Era questo che mi sfuggiva.
    Il tipo a livello più "alto" (e "astratto") ce l'hai già, è Prodotto.

    Quindi List<Prodotto> può contenere qualunque oggetto che deriva da Prodotto, anche in modo promiscuo. Poi chiaramente estrai "vedendo" gli oggetti solo come Prodotto ma bisogna vedere poi COSA ci devi fare con un List<Prodotto> .
  • Re: [Java] tipi generici con esempio pratico

    ?!?!?!?!?
    cioè......funziona lo stesso. Mi sono fatto due classi.
    Premetto la Generica:
    
    public class ProdottoPerLista <T>{
    	private T obj;
    
    	public ProdottoPerLista(T obj) {
    		setObj(obj);
    	}
    	
    	public T getObj() {
    		return obj;
    	}
    
    	public void setObj(T obj) {
    		this.obj = obj;
    	}
    }
    
    e poi le classi Inventario e Inventario2:
    
    import java.util.*;
    public class Inventario {
        List<ProdottoPerLista> list = new ArrayList<>();
        
        public void aggiungiOggetto() {
        	list.add(new ProdottoPerLista(new Monitor("HP", "LCD", 23)));
        	list.add(new ProdottoPerLista(new Notebook("ASUS","Intel",8)));
        	list.add(new ProdottoPerLista(new Stampante("CANON", "Laer")));
        }
        
        public void stampaLista() {
        	for (int i=0; i < list.size(); i++) {
        		System.out.println(list.get(i).getObj().toString());
        	}
        }
    }
    
    
    import java.util.*;
    public class Inventario2 {
        List<Prodotto> list = new ArrayList<>();
        
        public void aggiungiOggetto() {
        	list.add(new Monitor("HP", "LCD", 23));
        	list.add(new Notebook("ASUS","Intel",8));
        	list.add(new Stampante("CANON", "Laer"));
        }
        
        public void stampaLista() {
        	for (int i=0; i < list.size(); i++) {
        		System.out.println(list.get(i).toString());
        	}
        }
    }
    
    in entramibi i casi ho lo stesso output:
    
    public class TestGeneric {
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		Inventario i = new Inventario();
    		i.aggiungiOggetto();
    		i.stampaLista();
    
    		Inventario2 i2 = new Inventario2();
    		i2.aggiungiOggetto();
    		i2.stampaLista();
    	}
    }
    
    Monitor [marca=HP, tipo=LCD, dimensioniPollici=23]
    Notebook [marca=ASUS, ram=8, cpu=Intel]
    Stampante [marca=CANON, tipo=Laer]
    
    Monitor [marca=HP, tipo=LCD, dimensioniPollici=23]
    Notebook [marca=ASUS, ram=8, cpu=Intel]
    Stampante [marca=CANON, tipo=Laer]
    
    Ma quindi, la classe generica a che serve? In questo caso a niente.
    Ma forse serve per mettere in un ArrayList oggetto diversi dipo che ne so...... Dipendenti, Automobili, Alberi, Panchine, AnimaleDomestico ???
    E non poteri dichiarare allora la list come List<Object> visto che tutte le classi sono sottoclassi di Object? E poi se mi servono i dati non faccio altro che fare un cast?

    Mi sfugge proprio il vantaggio dei Generics......a che servono!
  • Re: [Java] tipi generici con esempio pratico

    tagan ha scritto:


    
    public class ProdottoPerLista <T>{
    
    
        List<ProdottoPerLista> list = new ArrayList<>();
    
    Questa ProdottoPerLista NON ti serve. Anche perché tra l'altro l'hai usata "male". ProdottoPerLista è "generica", quindi dovresti usarla parametrizzata.
    Cioè:

    List<ProdottoPerLista<QuiCosaCiMetti>> list = ........

    ?? Appunto .....

    Se ci mettessi List<ProdottoPerLista<Prodotto>> allora NON sarebbe nulla di più utile rispetto a List<Prodotto>
  • Re: [Java] tipi generici con esempio pratico

    OK.
    Incomincio a capire. E' vero però che se aggiungo un'altra classe, funziona solo questo:
    
    public class Persona {
    	public String nome;
    	public String cognome;
    	public Persona(String nome, String cognome) {
    		this.nome=nome;
    		this.cognome=cognome;
    	}
    	@Override
    	public String toString() {
    		return "Persona [nome=" + nome + ", cognome=" + cognome + "]";
    	}    
    }
    
    import java.util.*;
    public class Inventario {
        List<ProdottoPerLista> list = new ArrayList<>();
        
        public void aggiungiOggetto() {
        	list.add(new ProdottoPerLista(new Monitor("HP", "LCD", 23)));
        	list.add(new ProdottoPerLista(new Notebook("ASUS","Intel",8)));
        	list.add(new ProdottoPerLista(new Stampante("CANON", "Laser")));
        ------->>>>> list.add(new ProdottoPerLista(new Persona("Tizio", "Caio"))); <<<<<-------
        }
        
        public void stampaLista() {
    		System.out.println("\nInventario \n");
        	for (int i=0; i < list.size(); i++) {
        		System.out.println(list.get(i).getObj().toString());
        	}
        }
    }
    
    Mentre se dichiaro List<Prodotto> , l'aggiunta dell'oggetto Persona va in errore anzi, non compila.

    Certo, bisognerebbe capire a cosa serve una lista così promiscua, ma questo è solo un esempio di studio che credo che nella realtà non accadrà mai.
    Perche aggiungere in una unica collection oggetti di diversa natura?

    Cmq, comincio a capire i Generic, almeno negli oggetti già inclusi nei framework. un mio oggetto generic, non so a che potrebbe servire......magari con l'esperienza.

    Grazie.

    PS: In questo modo ho eliminato anche gli warning..... aggiungendo la wild card in questo modo List<ProdottoPerLista<?>>
    Solo sul piano di studio, non di funzionalità, è corretto? Può essere migliorato?
    
    import java.util.*;
    public class Inventario {
        List<ProdottoPerLista<?>> list = new ArrayList<>();
        
        public void aggiungiOggetto() {
        	list.add(new ProdottoPerLista<>(new Monitor("HP", "LCD", 23)));
        	list.add(new ProdottoPerLista<>(new Notebook("ASUS","Intel",8)));
        	list.add(new ProdottoPerLista<>(new Stampante("CANON", "Laser")));
        	list.add(new ProdottoPerLista<>(new Persona("Tizio", "Caio")));
        }
        
        public void stampaLista() {
    		System.out.println("\nInventario \n");
        	for (int i=0; i < list.size(); i++) {
        		System.out.println(list.get(i).getObj().toString());
        	}
        }
    }
    Grazie mille.
    tagan
  • Re: [Java] tipi generici con esempio pratico

    tagan ha scritto:


    Certo, bisognerebbe capire a cosa serve una lista così promiscua, ma questo è solo un esempio di studio che credo che nella realtà non accadrà mai.
    Sì, per certi scenari PUÒ accadere (di avere una lista "promiscua").

    tagan ha scritto:


    Perche aggiungere in una unica collection oggetti di diversa natura?
    Caso "classico": classe (astratta) Solido che ha i metodi es. calcolaVolume() e calcolaSuperficie() con poi sottoclassi es. Cubo, Sfera, ecc...
    Puoi fare un List<Solido> e metterci dentro cubi, sfere, ecc... Poi puoi fare un metodo:

    public static double sommaVolumi(List<Solido> solidi)

    o ancora meglio (c'è un motivo ben preciso per <? extends Solido> ):

    public static double sommaVolumi(List<? extends Solido> solidi)

    e puoi calcolare la somma dei volumi SENZA sapere che tipi di oggetti ci sono nella lista, perché calcolaVolume() è già noto in Solido e si presume che nelle sottoclassi sia implementato appropriatamente.

    tagan ha scritto:


    Cmq, comincio a capire i Generic, almeno negli oggetti già inclusi nei framework. un mio oggetto generic, non so a che potrebbe servire......magari con l'esperienza.
    Una classe "generica" serve quando vuoi fare una classe che sia in grado di trattare tipi di tipo arbitrario e questo serve tipicamente quando hai classi che fanno sostanzialmente solo da "contenitore" e basta.

    Esempio che faccio solitamente: una classe Coppia per avere una coppia di valori dello stesso tipo:
    public class Coppia<T> {        // <---- classe "generica"
        private T primoValore;
        private T secondoValore;
    
        // metodi getter/setter e poco altro es. toString ecc...
    }
    Se fai
    Coppia<String> coppiaString = new Coppia<String>();
    ci puoi mettere solo oggetti String (e nient'altro)

    Se fai
    Coppia<Integer> coppiaString = new Coppia<Integer>();
    ci puoi mettere solo oggetti Integer (e nient'altro)

    Se fai
    Coppia<Prodotto> coppiaString = new Coppia<Prodotto>();
    ci puoi mettere solo oggetti che sono di tipo Prodotto (se non fosse abstract) e sottotipi.

    tagan ha scritto:


    PS: In questo modo ho eliminato anche gli warning..... aggiungendo la wild card in questo modo List<ProdottoPerLista<?>>
    No, NON va bene List<ProdottoPerLista<?>> perché ti "perdi" comunque informazioni e devi trattare gli oggetti come Object (a meno di cast).
  • Re: [Java] tipi generici con esempio pratico

    andbin ha scritto:


    No, NON va bene List<ProdottoPerLista<?>> perché ti "perdi" comunque informazioni e devi trattare gli oggetti come Object (a meno di cast).
    ?!?!?
    Allora continua a sfuggirmi la convenienza.
    Ho preso quell'uso della wild card dall'esempio trovato su internet :
    
    public class Acqua {
    	@Override
    	public String toString(){
    		return " una bottiglia d'acqua";
    	}
    }
    
    public class Vino {
    	@Override
    	public String toString(){
    		return " una bottiglia di vino";
    	}
    }
    
    public class Bottiglia<T> {
    	private T contenuto;
    	public Bottiglia(T t){
    		contenuto=t;
    	}
    	public T getContenuto() {
    		return contenuto;
    	}
    }
    
    public class BraccioAutomatico {
    ----->>>	public void prendiBottiglia(Bottiglia<?> bottiglia){   <<<-----
    		System.out.println("Ho preso"+bottiglia.getContenuto());
    	}
    }
    
    public class Demo {
    	public static void main(String[] args) {
    		Bottiglia<Acqua> bottiglia1= new Bottiglia<Acqua>(new Acqua());
    		Bottiglia<Vino>  bottiglia2= new Bottiglia<Vino>(new Vino());
    		BraccioAutomatico braccio = new BraccioAutomatico();
    		braccio.prendiBottiglia(bottiglia1);
    		braccio.prendiBottiglia(bottiglia2);
    	}
    }
    
    dove nella classe BraccioAutomatico , dichiara il parametro del metodo con Bottiglia<?> bottiglia.

    Se nella mia classe ProdottoPerLista avessi crato un metodo che faceva la somma dei prezzi dei prodotti, ci sarei riuscito senza cast.
    Ma avrebbe funzionato anche se nella classe astratta Prodotto avessi creato questo metodo e poi riscritto nelle singole classi?

    in quale caso conviene e in quale caso c'è il risparmio di codice.
    ......o sto correndo troppo?
    Cmq adesso ci provo, aggiungo l'attributo prezzo ad ogni classe e poi provo con il metodo dentro ProdottoPerLista

    .........già fatta la prova! Non funziona a meno di cast. quella classe ProdottoPerLista non serva a niente tranne che ad aggiungere oggetti di diverso tipo come se nell'ArrayList volessi avere un contenitore di oggetti ma a cui non hai accesso a meno di cast!

    lo stesso, per l'esempio preso da internet, aggiungendo un attributo qualsiasi alle classi Acqua e Vino non c'è modo di accedervi dalla classe bottiglia a meno di un cast.

    Ultima domanda: che differenza c'è con questo che mi hai scritto tu? List<Solido> solidi e List<? extends Solido> solidi
    nel libro che sto leggendo non si capisce nulla.
    Grazie mille , sei molto gentile.
    tagan
Devi accedere o registrarti per scrivere nel forum
8 risposte