Un tipo generico è una classe o un'interfaccia generica parametrizzata rispetto ai tipi. Questo permette al compilatore di imporre la sicurezza dei tipi sulla classe o sull'interfaccia.
Considera questa interfaccia Container non generica:
class Container {
private Object object;
public void set(Object object) { this.object = object; }
public Object get() { return object; }
}
Poiché accetta e restituisce tipi Object, funziona con qualsiasi tipo non primitivo.
Tuttavia, questo ha un costo, perché del codice potrebbe chiamare get aspettandosi degli Integer, mentre altro codice chiama set aggiungendo delle String, causando un'eccezione a runtime.
Una classe generica e un'interfaccia generica hanno i seguenti formati:
class ClassName<T1, T2, ..., Tn> { ... }
interface InterfaceName<T1, T2, ..., Tn> { ... }
La sezione dei parametri di tipo, delimitata da parentesi angolari (<>), che segue il nome della classe o dell'interfaccia, specifica i parametri di tipo (detti anche variabili di tipo) T1, T2, ..., e Tn.
Questi possono essere usati all'interno del corpo della classe o dell'interfaccia per ottenere la sicurezza dei tipi generici.
Ecco la versione generica di Container:
class Container<E> {
private E object;
public void set(E object) { this.object = object; }
public E get() { return object; }
}
Quando la creiamo, dobbiamo dichiarare quale tipo contiene e il compilatore imporrà questo vincolo:
// empty <> can infer from context
Container<String> stringContainer = new Container<>();
// compiler knows this is a String, so it is allowed
stringContainer.set("Some string");
// no cast needed, compiler knows it is a String
String result = stringContainer.get();
// this causes a compiler error:
stringContainer.set(42);