PREV Introduzione a java generics NEXT Subtyping & wildcards
Pagina in costruzione. Questa pagina è una rielaborazione, fatta prevalentemente di copia&incolla, delle slide relative ai generics disponibili sulla pagina dedicata.
Generics e Collections vanno a braccetto con altre nuove feature (introdotte da Java5):
La loro combinazione è sinergica: l'intero è maggiore della somma delle parti!
List<Integer> ints = Arrays.asList(1,2,3); int s = 0; for (int n : ints) { s += n; } assert s == 6;
Probabilmente il codice è comprensibile anche senza alcuna spiegazione …
List<Integer> ints = Arrays.asList(1,2,3);
for (int n : ints) { s += n; }
assert s == 6;
List ints = Arrays.asList(new Integer [] { new Integer(1), new Integer(2), new Integer(3) } ); int s = 0 ; for (Iterator it = ints.iterator() ; it.hasNext() ; ){ int n = ((Integer)it.next()).intValue(); s += n; } assert(s == 6);
Una classe o interfaccia può dichiarare di ricevere uno o più parametri di tipo:
List <String> words = new ArrayList<String>(); words.add("Hello "); words.add("world !"); String s = words.get(0) + words.get(1); assert s.equals("Hello world !");
Codice senza l'ausilio dei generics …
List words = new ArrayList(); words.ADD("Hello "); words.ADD("world !"); String s = (String)words.get(0) + (String)words.get(1); assert s.equals("Hello world !");
Il bytecode compilato dei due precedenti esempi è (grossomodo) identico → retro compatibilità. I generics sono implementati tramite la type erasure
compile time | run time |
---|---|
List<Integer> | List |
List<String> | List |
List<List<String» | List |
… | … |
I generics eseguono implicitamente il cast che deve essere esplicitato nella versione senza generics
Cast-iron guarantee: I cast impliciti aggiunti dalla compilazione dei generics non falliscono mai!(*) (*) si applica esclusivamente al caso in cui non vengano inviati dal compilatore dei “unchecked warnings”
Type erasure, come mai?
Generics VS C++ Template
Java | C++ |
---|---|
List<List<String» | List< List<String> > (con lo spazio!) |
Type erasure: una sola versione della classe | Expansion: n versioni della solita classe; una per ogni tipo definito a compile-time → code bloat (?ottimizzazioni?) |
8 tipi primitivi hanno un corrispondente “tipo reference” nel package java.lang
Primitivo | Reference |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
bool | Boolean |
char | Character |
Boxing → conversione da primitivo a reference
Unboxing → conversione da reference a primitivo
La conversione viene fatta in automatico
int e → new Integer(e) // boxing Integer e → e.intValue() // unboxing
Esempi
// OK public static int somma(List<Integer> ints){ int s = 0; for ( int n :ints) { s += n ; } return s; } //Troppe conversioni! Performance :-( public static Integer sommaInteger(List<Integer> ints) { Integer s = 0; for ( int n :ints) { s += n ; } return s; }
Se uno degli operandi è un reference type viene applicato l'unboxing. Poi
Si applica a vari operatori binari ( tra cui == )
Per i primitivi == signifca “uguaglianza dei valori”
Per i reference == signifca “stessa identità”
L ist<Integer> bigs = Arrays.asList(100,200,300); assert sommaInteger(bigs) == somma(bigs); assert sommaInteger(bigs) != sommaInteger(bigs); // non raccomandato
I generics funzionano solo con i reference
Interi da -128 a 127, caratteri da 0 a \u007f, Byte e Boolean possono essere cachati
List<Integer> smalls = Arrays.asList(1,2,3); assert sommaInteger(smalls) == somma(smalls);// 6 assert sommaInteger(smalls) == sommaInteger(smalls); // 6, non raccomandato
Posso assegnare null ad un primitivo? → NO
For (Pippo p: pippi) …
int[] ints = {1,2,3,4}; for (Integer i :ints) System.out.println(i);
class Lists { public static <T> List<T> toList(T[] arr){ List<T> list = new ArrayList<T>(); for (T elem: arr) list.add(elem); return list; } ...
List<Integer> ints = Lists.toList( new Integer[] {1,2,3}); List<String> strings = Lists.toList( new String[] {"ciao","mondo"});
Boxing e unboxing gratuiti!
Che noia inserire gli elementi nell'array!
public static <T> List<T> toList(T ... arr){ List<T> list = new ArrayList<T>(); for (T elem: arr) list.add(elem); return list; } ... List<Integer> ints = Lists.toList( 1,2,2); List<String> strings = Lists.toList( "ciao","mondo");
Abbiamo sostituito
Qualsiasi numero di argomenti può precedere il vararg, niente deve seguire il vararg
Attenzione! Il tipo T non viene sempre dedotto dal compilatore, a volte è necessario esplicitarlo
Lists.<Object>toList( 1,"mondo");
Non è detto che Integer e String abbiano in comune solo Object! (Serializable, Comprarable, …)
Possono essere abilitate tramite i fag della JVM -ea o -enableassertions Altrimenti stanno a dormire …