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

  • 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 con Object
  • viene creata solo una copia del metodo/classe

center

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:

modoeffetti
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>)