====== Alcune novità rispetto a java 1.4 ======
PREV [[java:generics:start]] NEXT [[java:generics:subtiping_wildcards]]
FIXME Pagina in costruzione. Questa pagina è una rielaborazione, fatta prevalentemente di copia&incolla, delle slide relative ai generics disponibili sulla [[java:generics:start|pagina dedicata]].
===== Cominciamo =====
Generics e Collections vanno a braccetto con altre nuove
feature (introdotte da Java5):
* boxing e unboxing;
* nuovo ciclo for;
* funzioni che accettano un numero variabile di argomenti.
La loro combinazione è sinergica: l'intero è maggiore della
somma delle parti!
==== Versione "cool" ====
List 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 ints =
Arrays.asList(1,2,3);
* List ed ArrayList sono parte del CollectionFramework. List è generico: è possibile scrivere List per indicare liste contenenti elementi di tipo E. List indica liste contenenti elementi di tipo Integer.
* Boxing ed unboxing automagicamente applicati.
* Il metodo statico Arrays.asList prende un numero variabile di argomenti.
for (int n : ints) {
s += n;
}
* Ciclo foreach: permette di associare ad una variabile i valori contenuti in un oggetto iterabile. Ad ogni iterazione si prende l'elemento successivo
assert s == 6;
* Lo statement assert permette di controllare che l'asserzione fatta sia corretta …
* Asserzione vera
* AssertionError
* ... disabilitate di default ...
==== Versione old fashion ====
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);
* Il precedente codice è meno leggibile del primo :-(
* Senza i generics non è possibile indicare che tipo di elementi vogliamo inserire nella lista sono necessari dei cast :-(
* Senza boxing ed unboxing è necessario eseguire manualmente la conversione esempio: .intValue()
* Senza funzioni ad argomenti variabili è necessario passare alla asList un array “preimpostato” :-(
* Senza il ciclo foreach è necessario istanziare un iteratore per scandire la lista :-(
* .. Si c'è anche un altro modo ...
===== Generics: Base =====
Una classe o interfaccia può dichiarare di ricevere uno o più
parametri di tipo:
* Sono scritti tra parentesi angolate ()
* I tipi attuali devono essere forniti
* quando si dichiara una variabile
* quando si istanzia un oggetto
List words = new ArrayList();
words.add("Hello ");
words.add("world !");
String s = words.get(0) + words.get(1);
assert s.equals("Hello world !");
* La classe ArrayList implementa l'interfaccia List
* words è una lista contenente stringhe
* Vengono inserite due stringhe e successivamente vengono recuperate
* ... tutto senza cast!
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 !");
==== Type erasure ====
Il bytecode compilato dei due precedenti esempi è (grossomodo) identico → retro compatibilità. I generics sono implementati tramite la [[java:generics:type_erasure|type erasure]]
^compile time^run time^
|List |List |
|List |List |
|List> |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?
* Semplicità → il bytecode è identico
* Dimensioni → c'è solo una classe List
* Evoluzione → il bytecode (*) è retro compatibile e le librerie con e senza generics possono coesistere
* E' possibile evolvere il proprio codice “con calma e con pazienza”
* (*) Solo se ricompilato per versioni vecchie di java
Generics VS C++ Template
^Java^C++^
| List> | List< List > (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?) |
==== Tipi reference ====
* Classi
* Istanze
* Array (tutti)
* Possono assumere il valore null
* Sono tutti fgli di Object
==== Tipi primitivi ====
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 e unboxing ====
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 ints){
int s = 0;
for ( int n :ints) { s += n ; }
return s;
}
//Troppe conversioni! Performance :-(
public static Integer sommaInteger(List ints)
{
Integer s = 0;
for ( int n :ints) { s += n ; }
return s;
}
==== Binary Numeric Promotion ====
Se uno degli operandi è un reference type viene applicato l'__unboxing__. Poi
* Se uno degli operandi è double anche l'altro viene promosso a double
* Se uno degli operandi è foat anche l'altro viene promosso a foat
* Se uno degli operandi è long anche l'altro viene promosso a long
* Altrimenti entrambi vengono promossi a int
Si applica a vari operatori binari ( tra cui == )
==== Pericolo boxing/uboxing ====
:!: Per i primitivi == signifca “uguaglianza dei valori”
:!: Per i reference == signifca “stessa identità”
L ist 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 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
==== Due paroline sul ciclo foreach ====
For (Pippo p: pippi) …
* Applicabile a istanze di java.lang.Iterable
* Applicabile ad array[]
* Esegue in automatico eventuali boxing ed unboxing
int[] ints = {1,2,3,4};
for (Integer i :ints)
System.out.println(i);
===== Metodi generici =====
class Lists {
public static List toList(T[] arr){
List list = new ArrayList();
for (T elem: arr) list.add(elem);
return list;
}
...
* Il metodo toList accetta un array di tipo T[] e ritorna un List per ogni tipo T
* Si deve indicare all'inizio della frma del metodo statico
* T è un parametro di tipo
* Ogni metodo che dichiara un parametro di tipo è un metodo generico
List ints = Lists.toList(
new Integer[] {1,2,3});
List strings = Lists.toList(
new String[] {"ciao","mondo"});
Boxing e unboxing gratuiti!
===== Varargs =====
Che noia inserire gli elementi nell'array!
public static List toList(T ... arr){
List list = new ArrayList();
for (T elem: arr) list.add(elem);
return list;
}
...
List ints = Lists.toList( 1,2,2);
List strings = Lists.toList( "ciao","mondo");
Abbiamo sostituito
* T[] con T …
* L'array[] con valori separati da virgola
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.
Non è detto che Integer e String abbiano in comune solo Object! (Serializable, Comprarable, …)
===== Asserzioni =====
Possono essere abilitate tramite i fag della JVM **-ea** o **-enableassertions** Altrimenti stanno a dormire ...
NEXT [[java:generics:subtiping_wildcards]]