====== 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.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 [[java:generics:subtiping_wildcards]]