I tipi generici sono un modello di programmazione che permette di definire, con una sola dichiarazione, un intero insieme di metodi o classi.
public class Valore<T>{ private final T val; public Valore(T val){this.val = val;} public T get(){return val;} public String getType(){return val.getClass().getName();}}
per definire un tipo generico della classe, si utilizza la sintassi a parentesi angolari dopo il nome della classe con il tipo generico da utilizzare public class Valore<T>
da quel punto, si usa il tipo generico come una classe qualsiasi
istanziare la classe generica
Valore\<Integer> i = new Valore<>(42);Valore\<String> s = new Valore<>("ciao");Valore<Valore\<String>> v = new Valore<>(s);//i get type di questi saranno: Integer, String, Valore
perché non usare Object?
a questo punto, perché non dovrei usare sempre Object per generalizzare al massimo?
perché non ci dà informazioni sui tipi che si usano e costringe a continui downcast
esempi: tipi generici e diversi tra loro
public class Coppia\<T>{ private T a,b; public Coppia(T a, T b){ this.a = a; this.b = b; }}
public class Coppia\<T, S>{ private T a; private S b; public Coppia(T a, s b){ this.a = a; this.b = b; }}
i tipi generici funzionano solo con tipi derivati (non con i primitivi)
estendere un’interfaccia generica con vincolo di comparabilità sul tipo generico
public interface MinMax<T extends Comparable<T>>{ T min(); T max();}public class MyClass<T extends Comparable<T>> implements MinMax<T>{}
si possono comparare con minimo e massimo due tipi generici solo se tra i due c’è un principio di ordinamento, quindi l’interfaccia prende in input solo tipi T o suoi sottotipi
<T extends Comparable<T>> si specifica anche nella classe che implementa l’interfaccia perché il vincolo deve essere specificato a tutti i livelli
definire un metodo generico
si può definire un metodo generico anche in una classe non generica
per definire un metodo generico con proprio tipo generico, è necessario anteporre il tipo generico tra parentesi angolari al tipo di ritorno
static public <T> void esamina(ArrayList<T> lista){}
tipi generici e sottotipi
un metodo non può normalmente prendere un sottotipo di un tipo generico, ma solo il tipo stesso
static public void esamina(ArrayList < Frutto> frutti){// non funziona se passo un ArrayList\<Arancia>}
ma posso vincolare meglio per fare in modo che funzioni
static publc < T extends Frutto> void esamina(ArrayList\<T> frutti)
per non creare problemi, non è quindi possibile fare l’upcasting (o, per esempio, si potrebbe aggiungere una pera a un insieme di mele)
upcasting con array
l’upcasting è però possibile a tempo di compilazione con gli array, ma a tempo di esecuzione si ottiene un’eccezione
il jolly ”?”
nel caso in cui non sia necessario utilizzare il tipo generico T nel corpo della classe o del metodo, è possibile usare il jolly?
public static void mangia (Arraylist<? extends Mangiabile> frutta){}
che è equivalente a
public static void mangia (Arraylist<T extends Mangiabile> frutta){}
ma senza la notazione del tipo che si utilizza.
esempio:
public class Punto<T extends Number>{ private T x; private T y; // bla bla bla public static void main(String[] args){ Punto<?> p = new Punto<Integer>(10, 42); System.out.println(p); p = new Punto<Double>(11.0, 43.5); <System.out.println(p); }}
dietro le quinte
dietro le quinte avviene la cancellazione del tipo.
quando il compilatore traduce il metodo o la classe in bytecode:
elimina la sezione del tipo parametrico e lo sostituisce con quello reale
per default il tipo generico viene sostituito conObject
viene creata solo una copia del metodo/classe
ottenere informazioni sull'istanza di un generico
per via della cancellazione del tipo, non possiamo conoscere il tipo generico a tempo di esecuzione
??? wildcard ?
PECS
Producer Extends, Consumer Supers
extends e super esistono per due necessità primarie: leggere da e scrivere in una collezione generica.
ci sono 3 modi:
modo
effetti
List<?> lista = new ArrayList<Number>();
non so nulla sul tipo, posso solo leggere ma non scrivere
List<? extends Number> l = new Arraylist<Number>();
ho bisogno di una lista in input che “produca” valori di T, ma non si possono aggiungere elementi a quella lista (voglio leggere i dati sulla lista) (posso fare operazioni su elementi esistenti ma non scriverne di nuovi)
List<? super Number> l = new ArrayList<Number>();
posso “consumare” elementi di tipo T - sia scrivere nella lista che leggere
comparable e super
perché non posso scrivere <T extends Comparable<T>>?
per esempio:
public class Frutto implements Comparable< Frutto>public class Pera extends Frutto implements Comparable< Pera> // non si può fare!!
non si può implementare due volte la stessa interfaccia - (se volessi ordinare una collezione di Pera, non potrei perché estende Comparable<Frutto>)