I Generics sono stati introdotti a partire dalla versione J2SE 5, il termine ha un significato di tipo parametrizzato. I tipi parametrizzati rivestono particolare importanza perché consentono di creare classi, interfacce e metodi per i quali il tipo di dato sul quale si opera può essere specificato come parametro. Parliamo quindi di classi, interfacce e metodi generici.
Attraverso un codice generico possiamo realizzare un algoritmo che astrae dal tipo di dato specifico sul quale opera. Ad esempio un algoritmo per l'ordinamento di entità numeriche è lo stesso qualunque sia il tipo numerico che si utilizza; il tipo numerico utilizzato rappresenta quindi un parametro.
Vediamo un semplice esempio. Supponiamo di voler modellare il comportamento di un braccio meccanico che deve afferrare un bottiglia con un determinato contenuto posta su un tavolo. Il comportamento del braccio sarà lo stesso qualunque sia il contenuto della bottiglia che afferra. Il movimento del braccio può essere visto come l'algoritmo indipendente dal tipo di bottiglia sul quale agisce. Il contenuto della bottiglia determina invece un parametro generico. Ora modelliamo il tutto in classi Java che facciano uso di Generics.
Iniziamo con il definire due semplici classi per le tipologie di contenuto di una bottiglia:
package it.html.java.generics;
public class Acqua {
@Override
public String toString(){
return " una bottiglia d'acqua";
}
}
public class Vino {
@Override
public String toString(){
return " una bottiglia di vino";
}
}
Definiamo quindi la classe Bottiglia
package it.html.java.generics;
public class Bottiglia<T> {
private T contenuto;
public Bottiglia(T t){
contenuto=t;
}
public T getContenuto() {
return contenuto;
}
}
Una classe Generics specifica l'uso di un parametro di tipo con la notazione "simbolo minore - carattere - simbolo maggiore",
nel nostro esempio, una convezione sintattica prevede di usare lettere maiuscole per il carattere e alcuni caratteri tipici sono T, E, V e K. Nel nostro caso abbiamo definito una bottiglia in cui il parametro variabile è rappresentato dal suo contenuto T
, il parametro T
può essere visto come un segnaposto. All'atto della creazione dell'oggetto sarà sostituito, come vedremo tra poco, da un tipo classe specifico.
Continuiamo con la definizione della classe relativa al braccio meccanico:
package it.html.java.generics;
public class BraccioAutomatico {
public void prendiBottiglia(Bottiglia<?> bottiglia){
System.out.println("Ho preso"+bottiglia.getContenuto());
}
}
Ignorate per il momento il significato preciso del carattere ?
Demo
package it.html.java.generics;
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);
}
}
Se eseguiamo il codice avremo come stampa sulla console:
Ho preso una bottiglia d'acqua
Ho preso una bottiglia di vino
Possiamo notare come in fase di creazione dell'oggetto Bottiglia
Acqua
Vino
BraccioAutomatico
quindi la stessa azione per due tipi di bottiglie differenti. Non siamo poi limitati all'uso di un solo parametro, possiamo infatti definire classi generiche con più parametri
public class GenericClass<T,E,K> {...}
Per garantire compatibilità con le versioni precedenti alla J2SE 5, l'implementazione in Java dei Generics viene realizzata attraverso la tecnica nota con il nome di Erasure
Object
Avviene quindi il cast appropriato da parte del compilatore che si preoccupa di gestire la compatibilità del tipo. In sostanza i Generics sono semplicemente un meccanismo del codice sorgente e non esistono parametri di tipi in fase di esecuzione.