Generics e Inferenza dei Tipi in Java
- L'inferenza dei tipi consente di semplificare la dichiarazione delle variabili generiche.
- A partire da JDK 7, è possibile utilizzare l'operatore diamante per abbreviare la sintassi di creazione delle istanze di classi generiche.
- L'inferenza del tipo di variabili locali, introdotta in JDK 10, può essere utilizzata con i generics per semplificare ulteriormente le dichiarazioni.
Inferenza dei Tipi con i Generics
A partire da JDK 7, è diventato possibile abbreviare la sintassi utilizzata per creare un'istanza di un tipo generico.
Per iniziare, consideriamo la seguente classe generica:
// Questa è una classe generica che accetta due argomenti di tipo.
class MiaClasse<T, V> {
T oggetto1;
V oggetto2;
// Il costruttore accetta due argomenti di tipo T e V.
MiaClasse(T o1, V o2) {
oggetto1 = o1;
oggetto2 = o2;
}
/* ... */
}
Prima di JDK 7, per creare un'istanza di MiaClasse
, avremmo dovuto utilizzare un'istruzione simile alla seguente:
MiaClasse<Integer, String> mioOggetto =
new MiaClasse<Integer, String>(98, "Una Stringa");
Qui, gli argomenti di tipo (che sono Integer
e String
) sono specificati due volte: prima, quando mioOggetto
viene dichiarato, e secondo, quando un'istanza di MiaClasse
viene creata tramite new
.
Da quando i generics sono stati introdotti da JDK 5, questa è la forma richiesta da tutte le versioni di Java precedenti a JDK 7. Sebbene non ci sia nulla di sbagliato, di per sé, questa forma è un po' più prolissa di quanto dovrebbe essere.
Nella clausola new
, il tipo degli argomenti di tipo può essere facilmente inferito dal tipo di mioOggetto
; pertanto, non c'è realmente alcun motivo per cui debbano essere specificati una seconda volta. Per affrontare questa situazione, JDK 7 ha aggiunto un elemento sintattico che consente di evitare la seconda specificazione.
Oggi la dichiarazione precedente può essere riscritta come mostrato qui:
MiaClasse<Integer, String> mioOggetto =
new MiaClasse<>(98, "Una Stringa");
Si noti che la porzione di creazione dell'istanza utilizza semplicemente la sintassi <>
, che è una lista di argomenti di tipo vuota.
Questo è chiamato operatore diamante (o diamond operator). Dice al compilatore di inferire gli argomenti di tipo necessari dal costruttore nell'espressione new
. Il vantaggio principale di questa sintassi di inferenza del tipo è che abbrevia quelle che sono a volte dichiarazioni piuttosto lunghe.
Il precedente esempio può essere generalizzato. Quando viene utilizzata l'inferenza del tipo, la sintassi di dichiarazione per un riferimento generico e la creazione dell'istanza ha questa forma generale:
nome_classe<lista_arg_tipo> nome_var = new nome_classe<>(lista_arg_cons);
Qui, la lista degli argomenti di tipo del costruttore nella clausola new
è vuota.
L'inferenza del tipo può anche essere applicata al passaggio di parametri. Ad esempio, se il seguente metodo viene aggiunto a MiaClasse
,
boolean ugualeA(MiaClasse<T, V> o) {
if(o.oggetto1 == o.oggetto1 && oggetto2 == o.oggetto2)
return true;
else
return false;
}
allora la seguente invocazione è legale:
if(mioOggetto.ugualeA(new MiaClasse<>(1, "test")))
System.out.println("Uguale");
In questo caso, gli argomenti di tipo per l'argomento passato a ugualeA()
possono essere inferiti dal tipo del parametro.
Inferenza del Tipo di Variabili Locali e Generics
Come appena spiegato, l'inferenza del tipo è già supportata per i generics attraverso l'uso dell'operatore diamante.
Tuttavia, è anche possibile utilizzare la funzionalità di inferenza del tipo di variabile locale aggiunta da JDK 10 con una classe generica. Per esempio, assumendo MiaClasse
utilizzata nella sezione precedente, questa dichiarazione:
MiaClasse<Integer, String> mioOggetto =
new MiaClasse<Integer, String>(98, "Una Stringa");
può essere riscritta così utilizzando l'inferenza del tipo di variabile locale:
var mioOggetto =
new MiaClasse<>(98, "Una Stringa");
In questo caso, il tipo di mioOggetto
viene inferito e risulta essere MiaClasse<Integer, String>
perché quello è il tipo del suo inizializzatore.
Si noti anche che l'uso di var
risulta in una dichiarazione più breve di quanto sarebbe altrimenti. In generale, i nomi dei tipi generici possono spesso essere piuttosto lunghi e (in alcuni casi) complicati. L'uso di var
è un altro modo per accorciare sostanzialmente tali dichiarazioni.