GENERICS
- Quando si utilizzano i Generics come parametri, il tipo utilizzato DEVE essere esattamente lo stesso nella dichiarazione e nell’inizializzazione, altrimenti si ottiene un errore di compilazione.
- List<Integer> intList = new ArrayList<Integer>(); -> OK
- List<Number> intList = new ArrayList<Integer>(); -> ERRORE o Il subtyping NON funziona per i parametri Generics
- Per ovviare a questa limitazione però Java mette a disposizione i parametri WILDCARD.
- Definendo, ad esempio, un parametro come List<?>” significa che esso può rappresentare una lista di qualsiasi tipo come List<Integer>, List<String>, ecc..
- Utilizzando le wildcard non ho nessuna limitazione ai tipi reali che posso assegnare alla reference.
- Esempio:
List<?> numList = new ArrayList<Integer>(); numList = new ArrayList<String>();
- Se si vuole limitare l’assegnamento solo ad alcuni sottotipi di una classe si può utilizzare la wildcard bounded, tramite la keyword extends:
-
List<? extends Number> numList = new ArrayList<Integer>(); numList = new ArrayList<String>(); // ERRORE DI COMPILAZIONE
- In questo caso sono ammesse SOLO liste di tipi che estendono Number
- Allo stesso modo si può utilizzare la keyword super per limitare i tipi che possono essere utilizzati a quelli che sono sovra classi di un determinato tipo: o List<? super Integer> intList = new ArrayList<Integer>();
- Non si possono dichiarare campi static di un tipo generic T. Si ottiene un errore di compilazione.
- Non si possono avere classi Exception che utilizzano i Generics. Si ottiene un errore di compilazione.
- Le keywords extends e super cambiano di significato quando applicate ai Generics. In questo caso extends X e super X COMPRENDONO anche X stesso.
- Una classe dichiarata con i Generics può essere utilizzata senza gli argomenti generics. In questo caso è utilizzata come un tipo raw.
- Esempio:
class A{ … }
A a = new A(); -> raw type
A<D, D> a = new A(); -> dichiarazione VALIDA (può essere utilizzata senza argomenti sul lato destro dell’assegnamento) - Attenzione a NON confondere i placeholder con le classi.
- Nell’esempio precedente X e Y sono i placeholder per i tipi Generics e non le classi dei tipi reali da passare come argomenti. D, invece, è una classe che viene utilizzata per dichiarare il tipo generics e che verrà sostituita nei relativi placeholder.
- Attenzione all’utlizzo della sintassi diamond <> perché può essere utilizzata solo quando il tipo generics può essere inferito dal compilatore, come ad esempio in una inizializzazione.
- Quando il tipo NON può essere inferito, il compilatore lo sostituisce con Object e questo può creare problemi se l’oggetto viene poi utilizzato da un metodo che si aspettavo un tipo diverso:
- List<Integer> l = new ArrayList<>(); -> Il tipo viene inferito
- myMethod(new ArrayList<>()); -> il tipo NON può essere inferito e, se myMethod si aspetta una List di oggetti di tipo diverso da Object, ottengo un errore di compilazione.
- Quando si definisce una classe con i generics, come ad esempio class Q<T> { … } :
- NON si possono definire reference static di tipo T
- NON si possono istanziare oggetti o array di tipo T con l’operatore new
- I dettagli sui tipi forniti ai Generics vengono eliminati con la type-erasure una volta compilato il codice.
- A runtime quindi un ArrayList<String> generics ed un ArrayList raw sono dello stesso tipo class
- Esempio
class TypeCheck { public static void main(String[] args) { Class c1 = new ArrayList<String>().getClass(); Class c2 = ArrayList.class; System.out.println(c1 == c2); } }
- Questo estratto di codice compila e stampa il valore true