Il contenuto di questo sito è rilasciato sotto licenza Creative Commons License se non specificato diversamente

Alcune novità rispetto a java 1.4

PREV Introduzione a java generics NEXT Subtyping & wildcards

FIXME Pagina in costruzione. Questa pagina è una rielaborazione, fatta prevalentemente di copia&incolla, delle slide relative ai generics disponibili sulla 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<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);
  • List ed ArrayList sono parte del CollectionFramework. List è generico: è possibile scrivere List<E> per indicare liste contenenti elementi di tipo E. List<Integer> 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 (<T>)
  • I tipi attuali devono essere forniti
    • quando si dichiara una variabile
    • quando si istanzia un oggetto
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 !");
  • La classe ArrayList<E> implementa l'interfaccia List<E>
  • 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 type erasure

compile timerun 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?

  • 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

JavaC++
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?)

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<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;
}

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<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

Due paroline sul ciclo foreach

For (Pippo p: pippi) …

  • Applicabile a istanze di java.lang.Iterable<T>
  • 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 <T> List<T> toList(T[] arr){
List<T> list = new ArrayList<T>();
for (T elem: arr) list.add(elem);
return list;
}
...
  • Il metodo toList accetta un array di tipo T[] e ritorna un List<T> per ogni tipo T
  • Si deve indicare <T> all'inizio della frma del metodo statico
  • T è un parametro di tipo
  • Ogni metodo che dichiara un parametro di tipo è un metodo generico
List<Integer> ints = Lists.toList(
new Integer[] {1,2,3});
List<String> strings = Lists.toList(
new String[] {"ciao","mondo"});

Boxing e unboxing gratuiti!

Varargs

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

  • 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.<Object>toList( 1,"mondo");

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 Subtyping & wildcards

 
java/generics/alcune_novita_rispetto_a_java_1.4.txt · Ultima modifica: 2012/01/17 17:51 da fcasadei
 
Recent changes RSS feed