Sei sulla pagina 1di 7

Appunti sui Generics Java

Prensiamo ad esempio il problema di voler definire il concetto di "coppia di


elementi", inteso come un insieme di due elementi dello stesso tipo.
Con la programmazione generica cio' puo' essere fatto cosi':
public class Coppia<T> {
private T primo;
private T secondo;

public Coppia(T primo, T secondo) {


this.primo = primo;
this.secondo = secondo;
}
public T getPrimo() {
return this.primo;
}
...
}

public class CoppiaTest {

public void main(String []args) {


Coppia<Persona> coppia;
Persona p1 = new Persona("Stanlio");
Persona p2 = new Persona("Olio");
coppia = new Coppia<Persona>(p1, p2);
System.out.println(coppia.getPrimo()+ " e " +coppia.getSecondo());
}
}

Osservazioni
Non e' difficile trovare una similitudine tra:
 il concetto di parametro formale/attuale inerente l'invocazione dei
metodi
 il concetto di tipo formale/attuale inerente la tipizzazione di classi
generiche.

Occorre pero' prestare attenzione a non dimenticare la prima delle


differenze:
 il legame tra parametri formali/attuali è operato dalla JVM a tempo di
esecuzione
 il legame tra tipi formali/attuale è operato dal compilatore a tempo di
compilazione.

Metodi generici
E' possibile definire anche metodi generici (cioe' parametrici rispetto ad un
tipo).
Un metodo generico definisce i parametri (formali) di tipo nella segnatura
del metodo, prima del tipo di ritorno.
static <T> int mioMetodo(Coppia<T> c, T obj)

Supponiamo di voler definire la classe UtilitaPerCoppie, che definisce un


insieme di metodi di utilita' per la classe Coppia Ad esempio vogliamo
definire il metodo statico scambiaElementi, che prende come parametro
una coppia c e ne scambia gli elementi.
Il parametro c e' una coppia di elementi il cui tipo e' parametrico!
public class UtilitaPerCoppia {

public static <T> void scambiaElementi(Coppia<T> c) {


T tmp;

tmp = c.getPrimo();
c.setPrimo(c.getSecondo());
c.setSecondo(tmp);
}
}

Approfondimenti sui generics


Tipi parametrici multipli
E' possibile definire classi, interfacce e metodi generici con piu' parametri di
tipo.
Sintatticamente, si separano i vari parametri con una virgola:
public class Esempio<T, S> {...}
Tipi primitivi
Non e' possibile utilizzare i generics sui tipi primitivi, devono essere
utilizzate le classi wrapper.
public class TestCoppia {
public void main(String[] args) {
Coppia<Integer> coppia;
Integer i1 = new Integer(100);
Integer i2 = new Integer(200);
coppia = new Coppia<Integer>(i1, i2);
}
}

Convenzioni sui nomi


Esistono alcune convenzioni adottate anche nella documentazione
standard di java:
 <T>: Type (classe o interface)
 <S>: usato quando T è già in uso
 <E>: Elemento (molto usato nelle collezioni Java)
 <K>: chiave
 <V>: valore
 <N>: numero (una classe wrapper tra quelle usate per "oggettificare"
i valori numerici)

Notazioni particolari
Esiste anche la possibilita' di utilizzare dei caratteri "jolly" per definire
situazioni particolari.
Prensiamo, ad esempio, l'interfaccia List<E> e analizziamo il metodo
addAll: deve aggiungere alla collezione tutti gli elementi di un'altra
collezione che viene passata come parametro.

boolean addAll(Collection<? extends E> c)

Significa che c e' un oggetto Collection istanziato su E o su un qualsiasi


sottotipo di E.
Esempio:
List<Strumento> strumenti;
List<Chitarra> chitarre;
...
strumenti.addAll(chitarre);

Consideriamo ora la classe Collections (con la 's' finale): contiene un


insieme di operazioni molto utili per manipolare collezioni, come ad
esempio metodi che implementano ordinamento, ricerca, calcolo
dell'elemento max/min, inversione, ecc. ecc.
Consideriamo il metodo reverse(): esso inverte una collezione (il primo
elemento diventa l'ultimo, il secondo il penultimo, ecc):
static void reverse(List<?> lista)

Significa che lista e' un oggetto List istanziato su qualsiasi tipo (al quale
non e' necessario/utile associare un tipo formale con nome esplicito).
Esempio:
List<Strumento> strumenti;
List<Chitarra> chitarre;
...
Collections.reverse(chitarre);

Ancora dalla classe Collections, consideriamo il metodo fill(), che riempe gli
elementi della lista che viene passata come primo parametro, con un
oggetto, passato come secondo parametro:
static <T> void fill(List<? super T> list, T obj)

Significa che list e' un oggetto List istanziato su T o su un qualsiasi


supertipo di T
Esempio:
List<Strumento> strumenti;
Chitarra fender;
...
Collections.fill(strumenti, fender);

Infine vediamo un caso ancora un po' piu' complesso.


Ancora dalla classe Collections, consideriamo il metodo max(), che trova il
massimo di una collezione in base ad un criterio di ordinamento definito da
un "comparatore".
public static <T> T max(
Collection<? extends T> coll,
Comparator<? super T> comp
)

public static <T> T max(


Collection<? extends T> coll,
Comparator<? super T> comp
)

public interface Comparator<T> {


public int compare(T o1, T o2)
}

L'istruzione:
Collection<? extends T>

rappresenta un oggetto Collection istanziato su T o su un qualsiasi


sottotipo di T.
mentre l'istruzione:
Comparator<? super T>

rappresenta un oggetto Comparator istanziato su T o su un qualsiasi


supertipo di T.
Vediamo un esempio in cui T e' sostituito dal tipo Tamburo:
Esempio:
List<Tamburo> tamburi;

Comparator<Strumento> comp = new ComparatoreDiDb<Strumento>();

Tamburo rumoroso = Collections.max(tamburi, comp);


Questo perche' per ottenere il max di una collezione di un certo tipo basta
un comparatore che sappia dettare il criterio di ordinamento per un suo
qualsiasi supertipo.

Infine, un esempio ancora piu' difficile:


public static
<T extends Object & Comparable<? super T>> T max( Collection<? extends
T> coll )

restituisce il massimo elemento di una collezione, secondo l' ordinamento


naturale dei suoi elementi.
Tutti gli elementi della collezione devono implementare l'interfaccia
Comparable:
public interface Comparable<T> {
public int compareTo(T o)
}

Esempio: eliminazione dei duplicati da una


collezione
Un piccolo trucco nell'usare gli insiemi e' ottenibile con il seguente codice,
da usare se abbiamo una collezione c e vogliamo creare una seconda
collezione che contenga gli stessi elementi senza avere pero' duplicazioni:
Collection<Type> senzaDuplicati = new HashSet<Type>(c);

Il codice crea un Set, che contiene gli elementi inizialmente presenti in c,


eliminando per definizione i duplicati.
Una variante consente di mantenere l'ordine della collezione originale:
Collection<Type> senzaDuplicati = new LinkedHashSet<Type>(c);

Volendo e' possibile incapsulare tutto in un metodo che ritorna un Set dello
stesso tipo generico di quello dell'insieme in input:
public static <E> Set<E> removeDups(Collection<E> c) {
return new LinkedHashSet<E>(c);
}

Potrebbero piacerti anche