Java 1.5 (Tiger) Java 1.5 (Tiger) Versioni di Java Vers. Java 1.0 Java 1.1 Java 1.2 (o Java 2) Java 1.3 Java 1.4 Java 1.5 (o Java 5, o “Tiger”) Java 1.6 Anno 23 gennaio 1996 18 febbraio 1997 8 dicembre 1998 8 maggio 2000 dicembre 2002 1 ottobre 2004 2006? Java 1.5 (Tiger) Novità introdotte con Java 1.5 Tiger introduce molte novità sintattiche nel linguaggio (che era rimasto pressoché inalterato dalla prima versione): importazioni statiche varargs for-each autoboxing e unboxing tipi generici tipi enumerati altre novità (non discusse in questa lezione): annotazioni covarianza dei tipi di ritorno aggiornamento delle API standard Java 1.5 (Tiger) Parte I Importazioni statiche Java 1.5 (Tiger) Metodi e attributi statici In Java pre-Tiger, per invocare un metodo statico occorre qualificarlo con il nome della classe che lo contiene: y=Math . l o g ( x +3); z=C h a r a c t e r . i s D i g i t ( c ) ; i=I n t e g e r . p a r s e I n t ( s ) ; Lo stesso vale per gli attributi statici, siano essi finali (cioè, costanti) oppure no: a r e a=r a g g i o ∗ r a g g i o ∗Math . PI ; System . o u t . p r i n t l n ( " Ciao a tutti " ) ; Java 1.5 (Tiger) Importazione statica In Java Tiger, è possibile importare un membro (metodo o attributo) statico di una classe con lo scopo di evitare, in seguito, di doverlo qualificare con il nome della classe: im p or t s t a t i c j a v a . l a n g . C h a r a c t e r . i s D i g i t ; im p or t s t a t i c j a v a . l a n g . I n t e g e r . p a r s e I n t ; im p or t s t a t i c j a v a . l a n g . Math . ∗ ; im p or t s t a t i c j a v a . l a n g . System . o u t ; ... y=l o g ( x +3); z=i s D i g i t ( c ) ; i=p a r s e I n t ( s ) ; c i r c=r a g g i o ∗ r a g g i o ∗ PI ; o u t . p r i n t l n ( " Ciao a tutti " ) ; Java 1.5 (Tiger) Parte II Varargs Java 1.5 (Tiger) Esempio: funzione max a due argomenti La classe Math contiene un metodo per il calcolo del massimo fra due interi. Il metodo ha presumibilmente questo codice p u b l i c s t a t i c i n t max ( i n t x , i n t y ) { i f ( x > y ) return x ; else return y ; } Ecco un esempio di invocazione: im p or t s t a t i c j a v a . l a n g . Math . max ; ... c=max ( a , b ) ; Java 1.5 (Tiger) Esempio: max fra più argomenti E se volessi calcolare il massimo fra tre interi? c l a s s MyMath { ... p u b l i c s t a t i c i n t max ( i n t x , i n t y , i n t z ) { if ( x > y ) i f ( x > z ) return x ; else return z ; else i f ( y > z ) return y ; else return z ; } } Devo scrivere un metodo diverso per ogni possibile numero di argomenti??? Java 1.5 (Tiger) Varargs Con Java Tiger è possibile dichiarare che la funzione max prende un numero arbitrario di interi come argomento. Questa caratteristica viene chiamata varargs. c l a s s MyMath { p u b l i c s t a t i c i n t max ( i n t . . . x ) { blablabla } } x=max ( a , b , c ) ; y=max ( a , b , c , d , e ) ; Java 1.5 (Tiger) Varargs (cont.) Java considera questa situazione equivalente al passaggio di un array (cioè, in questo caso, l’argomento è a tutti gli effetti un array di interi): c l a s s MyMath { ... p u b l i c s t a t i c i n t max ( i n t . . . x ) { i n t max = I n t e g e r . MIN VALUE ; f o r ( i n t i = 0 ; i < x . l e n g t h ; i++ ) i f ( x [ i ] > max ) max = x [ i ] ; r e t u r n max ; } } Java 1.5 (Tiger) Varargs: restrizioni Un solo argomento può essere variabile, e deve sempre trattarsi dell’ultimo: //NO! public void f ( i n t . . . x , S t r i n g y , S t r i n g . . . z ) ; // S I public void g ( i n t x , S t r i n g y , S t r i n g . . . z ) ; All’invocazione, si può passare un qualunque numero (anche zero) di argomenti, oppure un array. Java 1.5 (Tiger) Parte III Interludio: le liste (pre-Tiger) Java 1.5 (Tiger) Interfaccia List L’interfaccia List(del pacchetto java.util) descrive una sequenza di oggetti, simile a un array di Object ma senza la restrizione di avere una dimensione prefissata. Nelle prossime slide vedremo un esempio di dichiarazione, creazione e uso di una lista, con l’obiettivo di mostrare le novità introdotte in Java Tiger. L’esempio che consideriamo è il seguente: vogliamo leggere una sequenza di frazioni, e poi calcolarne la somma. Java 1.5 (Tiger) Dichiarazione e creazione La dichiarazione della lista che conserverà le nostre frazioni è: im p or t j a v a . u t i l . ∗ ; ... List listaFrazioni ; Poiché List è solo un’interfaccia, per creare la lista bisognerà usare una sua implementazione. Una delle implementazioni disponibili si chiama LinkedList: l i s t a F r a z i o n i = new L i n k e d L i s t ( ) ; Java 1.5 (Tiger) Riempimento Ora riempiamo la lista, usando il metodo add(Object) che aggiunge un oggetto in fondo alla lista: do { i n t num = i n . r e a d I n t ( " Numeratore : " ) ; i n t den = i n . r e a d I n t ( " Denominatore : " ) ; F r a z i o n e f = new F r a z i o n e ( num , den ) ; l i s t a F r a z i o n i . add ( f ) ; } w h i l e ( i n . r e a d S i N o ( " Ancora una frazione ? " ) ) ; Java 1.5 (Tiger) Scorrimento (I) Il primo modo per scorrere la lista consiste nell’usare in modo combinato il metodo size() (che restituisce il numero di elementi nella lista) e il metodo get(int) che restituisce l’elemento dato il suo indice (fra 0 e la dimensione meno 1): F r a z i o n e somma = new F r a z i o n e ( 0 ) ; f o r ( i n t i = 0 ; i < l i s t a F r a z i o n i . s i z e ( ) ; i++ ) { Frazione f = ( Frazione ) ( l i s t a F r a z i o n i . get ( i ) ) ; somma = somma . p i u ( f ) ; } Notate il casting: è indispensabile, poiché il metodo get(int) restituisce un Object. . . Java 1.5 (Tiger) Scorrimento (II) Le strutture come List forniscono in alternativa un iteratore che può essere usato quando le si voglia scorrere sequenzialmente. Un iteratore (interfaccia Iterator di java.util) dà i metodi next() (che restituisce il prossimo oggetto dell’iterazione, “consumandolo”) e hasNext() (che dice se c’è un prossimo oggetto). L’iteratore per scorrere la lista è fornito invocando il metodo iterator() di List. F r a z i o n e somma = new F r a z i o n e ( 0 ) ; Iterator it = listaFrazioni . iterator (); while ( i t . hasNext ( ) ) { Frazione f = ( Frazione ) ( i t . next () ) ; somma = somma . p i u ( f ) ; } Java 1.5 (Tiger) Scorrimenti a confronto F r a z i o n e somma = new F r a z i o n e ( 0 ) ; f o r ( i n t i = 0 ; i < l i s t a F r a z i o n i . s i z e ( ) ; i++ ) { Frazione f = ( Frazione ) ( l i s t a F r a z i o n i . get ( i ) ) ; somma = somma . p i u ( f ) ; } F r a z i o n e somma = new F r a z i o n e ( 0 ) ; Iterator it = listaFrazioni . iterator (); while ( i t . hasNext ( ) ) { Frazione f = ( Frazione ) ( i t . next () ) ; somma = somma . p i u ( f ) ; } Il secondo è più efficiente (e più bello). Java 1.5 (Tiger) Parte IV For-each Java 1.5 (Tiger) Iterabilità Lo scorrimento mediante iteratore è cosı̀ comune che Tiger ha introdotto uno speciale tipo di ciclo applicabile a tutte le strutture che abbiano un metodo iterator() che restituisce un iteratore per scorrerle. Queste strutture si chiamano iterabili (implementano la nuova interfaccia Iterable). Questo nuovo ciclo, chiamato for-each, ha la seguente sintassi f o r ( <d i c h . v a r i a b i l e > : <ogg . i t e r a b i l e > ) <i s t r u z i o n e > Java 1.5 (Tiger) Scorrimento mediante for-each Il vecchio ciclo: F r a z i o n e somma = new F r a z i o n e ( 0 ) ; Iterator it = listaFrazioni . iterator (); while ( i t . hasNext ( ) ) { Frazione f = ( Frazione ) ( i t . next () ) ; somma = somma . p i u ( f ) ; } diventa: F r a z i o n e somma = new F r a z i o n e ( 0 ) ; for ( Object x : l i s t a F r a z i o n i ) { Frazione f = ( Frazione ) x ; somma = somma . p i u ( f ) ; } Java 1.5 (Tiger) Scorrimento mediante for-each di array Il for-each si può applicare anche agli array: i n t max = I n t e g e r . MIN VALUE ; f o r ( i n t i = 0 ; i < x . l e n g t h ; i++ ) i f ( x [ i ] > max ) max = x [ i ] ; r e t u r n max ; } diventa: i n t max = I n t e g e r . MIN VALUE ; for ( int y : x ) i f ( y > max ) max = y ; r e t u r n max ; Java 1.5 (Tiger) Casi in cui il for-each non si usa Il for-each dà un accesso in lettura alla struttura su cui si itera (p.es., all’array). Quindi, ad esempio, il seguente frammento di codice (che incrementa gli elementi pari di un array di interi) non si può trasformare in un for-each: int a [ ] ; ... f o r ( i n t i = 0 ; i < a . l e n g t h ; i++ ) i f ( a [ i ] % 2 == 0 ) a [ i ]++; Java 1.5 (Tiger) Casi in cui il for-each non si usa Il fatto che non si possa modificare il contenuto dell’array mediante un for-each non significa che non si possa cambiare lo stato interno degli oggetti contenuti: Rettangolo arrayDiRettangoli [ ] ; ... for ( Rettangolo r : arrayDiRettangoli ) r . setBase ( 5 ) ; Java 1.5 (Tiger) Casi in cui il for-each non si usa Durante il for-each non si ha accesso all’indice di scorrimento: quindi non lo si può usare per scorrere degli array in parallelo. . . S t r i n g nome [ ] ; int eta [ ] ; ... f o r ( i n t i = 0 ; i < nome . l e n g t h ; i++ ) i f ( e t a [ i ] < 18 ) o u t . p r i n t l n ( nome [ i ] + " è minorenne " ) ; else o u t . p r i n t l n ( nome [ i ] + " è maggiorenne " ) ; Java 1.5 (Tiger) Casi in cui il for-each non si usa Per lo stesso motivo, è impossibile controllare le uscite anticipate dai cicli (a meno di non usare un flag) e quindi è sconsigliabile usare il for-each per casi del genere: int x [ ] ; ... int i ; f o r ( i = 0 ; i < x . l e n g t h ; i++ ) i f ( x [ i ] % 2 == 0 ) b r eak ; i f ( i < x . length ) o u t . p r i n t l n ( "L ’ array contiene un valore pari " ) ; else o u t . p r i n t l n ( "L ’ array contiene valori dispari " ) ; Java 1.5 (Tiger) Parte V Autoboxing e unboxing Java 1.5 (Tiger) Liste di cosa? Una List può contenere qualunque oggetto: il metodo add(Object) permette di aggiungere alla lista oggetti arbitrari, eventualmente anche di tipo eterogeneo: List listaMista ; l i s t a M i s t a = new L i n k e d L i s t ( ) ; l i s t a M i s t a . add ( new F r a z i o n e ( 3 , 73 ) ) ; l i s t a M i s t a . add ( " pippo " ) ; l i s t a M i s t a . add ( new R e t t a n g o l o ( 5 , 10 ) ) ; Cosa non si può aggiungere a una lista? Non si possono aggiungere tipi primitivi. . . (ovvio, non sono oggetti!): l i s t a M i s t a . add ( 3 ) ; // NON COMPILA l i s t a M i s t a . add ( 3 . 5 ) ; // NON COMPILA l i s t a M i s t a . add ( f a l s e ) ; // NON COMPILA Java 1.5 (Tiger) Perché? Tradizionalmente, in Java pre-Tiger esiste una distinzione netta e invalicabile fra il mondo degli oggetti (i “tipi riferimento”) e il mondo dei “tipi primitivi”. A fare da ponte esistono solo le classi involucro: l i s t a M i s t a . add ( new I n t e g e r ( 3 ) ) ; l i s t a M i s t a . add ( new Double ( 3 . 5 ) ) ; l i s t a M i s t a . add ( new B o o l e a n ( f a l s e ) ) ; Java 1.5 (Tiger) Somma di interi Riscriviamo l’esempio del calcolo della somma per gli interi: L i s t l i s t a I n t e r i = new L i n k e d L i s t ( ) ; do { i n t v a l = i n . r e a d I n t ( " Valore : " ) ; l i s t a I n t e r i . add ( new I n t e g e r ( v a l ) ) ; } w h i l e ( i n . r e a d S i N o ( " Ancora un intero ? " ) ) ; i n t somma = 0 ; Iterator it = listaInteri . iterator (); while ( i t . hasNext ( ) ) { I n t e g e r v = ( I n t e g e r ) ( i t . next () ) ; somma = somma + v . i n t V a l u e ( ) ; } Java 1.5 (Tiger) Osservazioni Notate nel codice: la creazione di un oggetto intero per “avvolgere” il valore (boxing): l i s t a I n t e r i . add ( new I n t e g e r ( v a l ) ) ; la necessità di invocare un metodo per “guardare dentro” l’oggetto (unboxing): somma = somma + v . i n t V a l u e ( ) ; Java 1.5 (Tiger) In Java Tiger. . . Java Tiger è in grado di dedurre autonomamente quando serve un boxing/unboxing: quindi l i s t a I n t e r i . add ( new I n t e g e r ( v a l ) ) ; ... somma = somma + v . i n t V a l u e ( ) ; diventano l i s t a I n t e r i . add ( v a l ) ; ... somma = somma + v ; Java 1.5 (Tiger) Somma di interi (2) Riscriviamo l’esempio del calcolo della somma per gli interi: L i s t l i s t a I n t e r i = new L i n k e d L i s t ( ) ; do { i n t v a l = i n . r e a d I n t ( " Valore : " ) ; l i s t a I n t e r i . add ( v a l ) ; } w h i l e ( i n . r e a d S i N o ( " Ancora un intero ? " ) ) ; i n t somma = 0 ; Iterator it = listaInteri . iterator (); while ( i t . hasNext ( ) ) { I n t e g e r v = ( I n t e g e r ) ( i t . next () ) ; somma = somma + v ; } Java 1.5 (Tiger) Alcune conseguenze L’autoboxing/unboxing permettono di scrivere codice che mescola tipi primitivi con le classi wrapper in modo più o meno arbitrario: Integer x = 0; w h i l e ( x < 500 ) x++; In effetti, Java sta modificando il codice “dietro le quinte”: I n t e g e r x = new I n t e g e r ( 0 ) ; w h i l e ( x . i n t V a l u e ( ) < 500 ) x = new I n t e g e r ( x . i n t V a l u e ( ) + 1 ) ; Ma, attenzione all’efficienza! Java 1.5 (Tiger) Parte VI Tipi generici Java 1.5 (Tiger) Warning L i s t l i s t a F r a z i o n i = new L i n k e d L i s t ( ) ; do { i n t num = i n . r e a d I n t ( " Numeratore : " ) ; i n t den = i n . r e a d I n t ( " Denominatore : " ) ; l i s t a F r a z i o n i . add ( new F r a z i o n e ( num , den ) ) ; } w h i l e ( i n . r e a d S i N o ( " Ancora una frazione ? " ) ) ; F r a z i o n e somma = new F r a z i o n e ( 0 ) ; f o r ( i n t i = 0 ; i < l i s t a F r a z i o n i . s i z e ( ) ; i++ ) { Frazione f = ( Frazione ) ( l i s t a F r a z i o n i . get ( i ) ) ; somma = somma . p i u ( f ) ; } Compilando con Java 1.4, non dà errori. Con Java 1.5, dà degli warning: Note: Esempio.java uses unchecked or unsafe operations. Recompile with -Xlint:unchecked for details. Java 1.5 (Tiger) Perché Il motivo è che le classi come List sono inerentemente insicure: chi le usa può creare liste contenenti elementi arbitrari e questo rende il loro uso potenzialmente pericoloso. Infatti, chiunque può surrettiziamente inserire nella lista un oggetto che non è una frazione. L’effetto, in tal caso, è che la conversione Frazione f = ( Frazione ) ( l i s t a F r a z i o n i . get ( i ) ) ; dà una ClassCastException. Java 1.5 (Tiger) Tipi generici: dichiarazione Java introduce i tipi generici: classi e interfacce che dipendono da uno o più parametri (a loro volta, una classe o interfaccia). Ad esempio, l’interfaccia List ora è generica: im p or t j a v a . u t i l . ∗ ; ... L i s t <F r a z i o n e > l i s t a F r a z i o n i ; Java 1.5 (Tiger) Tipi generici: costruzione Anche le implementazioni di List sono generiche, cioè parametrizzate sulla base di un tipo: l i s t a F r a z i o n i = new L i n k e d L i s t <F r a z i o n e > ( ) ; A questo punto, potremo aggiungere alla lista solo frazioni, il metodo get(int)resituisce solo frazioni eccetera. Java 1.5 (Tiger) Somma frazioni con Tiger L i s t <F r a z i o n e > l i s t a F r a z i o n i = new L i n k e d L i s t <F r a z i o n e > ( do { i n t num = i n . r e a d I n t ( " Numeratore : " ) ; i n t den = i n . r e a d I n t ( " Denominatore : " ) ; l i s t a F r a z i o n i . add ( new F r a z i o n e ( num , den ) ) ; } w h i l e ( i n . r e a d S i N o ( " Ancora una frazione ? " ) ) ; F r a z i o n e somma = new F r a z i o n e ( 0 ) ; f o r ( i n t i = 0 ; i < l i s t a F r a z i o n i . s i z e ( ) ; i++ ) { Frazione f = l i s t a . get ( i ) ; somma = somma . p i u ( f ) ; } Java 1.5 (Tiger) Ciclo con iteratore Con le liste generiche, iterator() restituisce un iteratore di tipo specifico: F r a z i o n e somma = new F r a z i o n e ( 0 ) ; I t e r a t o r <F r a z i o n e > i t = l i s t a F r a z i o n i . i t e r a t o r ( ) ; while ( i t . hasNext ( ) ) { Frazione f = i t . next ( ) ; somma = somma . p i u ( f ) ; } . . . oppure, addirittura F r a z i o n e somma = new I t e r a t o r <F r a z i o n e > i t while ( i t . hasNext ( ) somma = somma . p i u ( Frazione ( 0 ); = listaFrazioni . iterator (); ) i t . next () ) ; Java 1.5 (Tiger) Ciclo con for-each Oppure, usando il for-each: L i s t <F r a z i o n e > l i s t a F r a z i o n i = new L i n k e d L i s t <F r a z i o n e > ( do { i n t num = i n . r e a d I n t ( " Numeratore : " ) ; i n t den = i n . r e a d I n t ( " Denominatore : " ) ; l i s t a F r a z i o n i . add ( new F r a z i o n e ( num , den ) ) ; } w h i l e ( i n . r e a d S i N o ( " Ancora una frazione ? " ) ) ; F r a z i o n e somma = new F r a z i o n e ( 0 ) ; for ( Frazione f : l i s t a F r a z i o n i ) somma = somma . p i u ( f ) ; Java 1.5 (Tiger) E List? Che ruolo ha la vecchia List in Java Tiger? Esiste per motivi di compatibilità all’indietro. Il suo uso produce degli warning. Se si vogliono veramente usare liste eterogenee si dovrebbe usare List<Object> L i s t <O b j e c t > l i s t a M i s t a = new L i n k e d L i s t <O b j e c t > ( ) ; l i s t a M i s t a . add ( " ciao " ) ; l i s t a M i s t a . add ( new F r a z i o n e ( 4 , 7 ) ) ; l i s t a M i s t a . add ( 5 ) ; for ( Object x : l i s t a M i s t a ) out . p r i n t l n ( x ) ; Java 1.5 (Tiger) Classi generiche come argomenti: tipo specifico La classe generica List<T> è un insieme di classi, una per ogni possibile tipo T. All’atto dell’uso, si deve fissare il tipo T. Classi generiche possono essere usate come argomenti di metodi. Ad esempio, il seguente metodo calcola la somma di una lista di interi: p u b l i c s t a t i c i n t somma ( L i s t <I n t e g e r > l i s t ) { int s = 0; f o r ( i n t x : l i s t ) s += x ; return s ; } Quando si invoca il metodo, occorre passargli una lista di interi. Java 1.5 (Tiger) Classi generiche come argomenti: tipo arbitrario In alcuni casi, si vuole poter accettare una lista qualunque sia il suo tipo di base. In tal caso, si usa la sintassi speciale List<?> che indica “una lista di qualunque cosa”. Ad esempio, il seguente metodo statico stampa il contenuto di una lista qualsiasi: p u b l i c s t a t i c v o i d s t a m p a L i s t a ( L i s t <?> l i s t ) { C o n s o l e O u t p u t M a n a g e r o u t = new C o n s o l e O u t p u t M a n a g e r ( ) ; f o r ( Object x : l i s t ) out . p r i n t l n ( x ) ; } Quando si invoca il metodo, occorre passargli una lista qualsiasi. Java 1.5 (Tiger) Classi generiche come argomenti: tipo arbitrario (cont.) Si potrebbe in alternativa usare una lista non generica: public s t a t i c void stampaLista ( L i s t l i s t ) { C o n s o l e O u t p u t M a n a g e r o u t = new C o n s o l e O u t p u t M a n a g e r ( ) ; f o r ( Object x : l i s t ) out . p r i n t l n ( x ) ; } . . . ma questo produrrebbe warning. Java 1.5 (Tiger) Classi generiche come argomenti: tipo ripetuto Supponiamo di voler scrivere un metodo che cerca un dato elemento in una lista: il tipo dell’elemento e il tipo di base della lista devono coincidere. Per specificare questo si usa una sintassi speciale: p u b l i c s t a t i c <T> b oolean p r e s e n t e ( L i s t <T> l i s t , T x ) { for ( T y : l i s t ) i f ( y . equals ( x ) ) return true ; return f a l s e ; } Vuole dire: “per qualunque T, il metodo prende una lista di T e un argomento di tipo T”. Java 1.5 (Tiger) Classi generiche come argomenti: tipo ripetuto (cont.) Un altro esempio analogo è il seguente: p u b l i c s t a t i c <T> T mediano ( L i s t <T> l i s t ) { return l i s t . get ( l i s t . s i z e () / 2 ) ; } Vuole dire: “per qualunque T, il metodo prende una lista di T e restituisce un T”. Java 1.5 (Tiger) Classi generiche come argomenti: tipo vincolato In alcuni casi, si vuole che un metodo accetti come argomento un tipo generico il cui tipo di base sia vincolato a essere sottoclasse di qualcosa, o a implementare una qualche interfaccia. In tali casi, si usa la sintassi <? extends Something> che significa “qualunque cosa che estenda (o implementi) Something”. Il seguente esempio somma le aree di una lista il cui tipo di base sia Figura o un qualunque suo sottotipo: p u b l i c s t a t i c d ou b le sommaAree ( L i s t <? e x t e n d s F i g u r a > l i s t ) { d ou b le s = 0 ; f o r ( F i g u r a f : l i s t ) s += f . g e t A r e a ( ) ; return s ; } Si potrà invocare passandogli una List<Figura>, una List<Rettangolo>, una List<Cerchio> eccetera. Java 1.5 (Tiger) Tipi generici e gerarchia dei tipi Dal punto di vista della gerarchia dei tipi, LinkedList<Rettangolo> è un sottotipo di List<Rettangolo> e LinkedList<Quadrato> è sottotipo di List<Quadrato>. Ma LinkedList<Quadrato> non è sottotipo di LinkedList<Rettangolo>! Quindi, ad esempio, questo è vietato: L i s t <R e t t a n g o l o > l r ; L i s t <Quadrato > l q ; ... l r =l q ; // NO benché questo sia ovviamente consentito l r . add ( new Qua dra to ( 5 , 6 ) ) ; // OK Java 1.5 (Tiger) Scrittura di classi generiche Come primo esempio di classe generica vogliamo realizzare una semplice coda: una coda è una struttura a cui si possono aggiungere elementi, mediante un metodo di nome accoda(); in ogni istante è possibile sapere quanti elementi ci sono in coda, mediante un metodo di nome size(); infine, il metodo servi() restituisce il prossimo elemento da servire, e lo toglie dalla coda. Gli elementi vengono serviti nell’ordine in cui si erano messi in coda (FIFO=First-In/First-Out). Java 1.5 (Tiger) Coda semplice c l a s s Coda <T> { } c l a s s Coda <T> { p r o t e c t e d L i s t <T> l i s t ; p u b l i c Coda ( ) { l i s t = new L i n k e d L i s t <T> ( ) ; } } c l a s s Coda <T> { p r o t e c t e d L i s t <T> l i s t ; p u b l i c Coda ( ) { l i s t = new L i n k e d L i s t <T> (Java ) ; 1.5 (Tiger) Coda semplice: test Coda<S t r i n g > c o d a S e m p l i c e = new Coda<S t r i n g > ( ) ; c o d a S e m p l i c e . a c c o d a ( " Paolo Boldi " ) ; c o d a S e m p l i c e . a c c o d a ( " Carlo Mereghetti " ) ; c o d a S e m p l i c e . a c c o d a ( " Walter Cazzola " ) ; c o d a S e m p l i c e . a c c o d a ( " Dario Malchiodi " ) ; consumaCoda ( c o d a S e m p l i c e ) ; p u b l i c s t a t i c v o i d consumaCoda ( Coda<?> coda ) { C o n s o l e O u t p u t M a n a g e r o u t = new C o n s o l e O u t p u t M a n a g e r ( ) ; f o r ( i n t n = coda . s i z e ( ) ; n > 0 ; n−− ) o u t . p r i n t l n ( coda . s e r v i ( ) ) ; } Java 1.5 (Tiger) Scrittura di classi generiche Vogliamo ora estendere Coda con una classe in cui gli elementi vengano serviti non in ordine di arrivo (FIFO), ma rispetto a una qualche relazione di ordine naturale definita su di essi. Ad esempio, se si tratta di stringhe, verranno servite in ordine alfabetico, se si tratta di frazioni in ordine crescente di valore, ecc. Ovviamente, non potremo fare più code di elementi arbitrari, ma dovrà per forza trattarsi di elementi confrontabili, cioè che implementino l’interfaccia Comparable. Notate che quest’ultima è, in Tiger, un’interfaccia generica. Java 1.5 (Tiger) Coda ordinata c l a s s CodaOrd<T e x t e n d s Comparable <T>> e x t e n d s Coda<T> { } c l a s s CodaOrd<T e x t e n d s Comparable <T>> e x t e n d s Coda<T> { p r i v a t e b oolean f o r s e D i s o r d i n a t a ; p u b l i c CodaOrd ( ) { forseDisordinata = false ; } p u b l i c void accoda ( T x ) { super . accoda ( x ) ; f o r s e D i s o r d i n a t a = true ; } } Java 1.5 (Tiger) Coda ordinata: test CodaOrd<S t r i n g > c o d a O r d i n a t a = new CodaOrd<S t r i n g > ( ) ; c o d a O r d i n a t a . a c c o d a ( " Paolo Boldi " ) ; c o d a O r d i n a t a . a c c o d a ( " Carlo Mereghetti " ) ; c o d a O r d i n a t a . a c c o d a ( " Walter Cazzola " ) ; c o d a O r d i n a t a . a c c o d a ( " Dario Malchiodi " ) ; consumaCoda ( c o d a O r d i n a t a ) ; T e s t d e l l a c l a s s e CodaOrd Carlo Mereghetti Dario Malchiodi Paolo B o l d i Walter Cazzola Java 1.5 (Tiger) Parte VII Tipi enumerati Java 1.5 (Tiger) Tipi enumerati in Java pre-Tiger Nella pratica della programmazione può capitare di dover usare variabili che possono assumere un numero finito, piccolo e noto a priori di valori possibili. Per esempio: giorni della settimana, mesi dell’anno ecc. Consideriamo ad esempio la classe CartaDaGioco: c l a s s CartaDaGioco { p u b l i c i n t seme ; public int valore ; p u b l i c CartaDaGioco ( i n t s , i n t v ) { seme = s ; valore = v ; } ... } Java 1.5 (Tiger) Tipi enumerati in Java pre-Tiger Concentriamoci sull’attributo seme. Idealmente vorremmo che seme potesse assumere solo quattro valori (cuori, quadri, fiori, picche). Identificheremo questi valori con i numeri 0, 1, 2, 3 rispettivamente. c l a s s CartaDaGioco { p u b l i c i n t seme ; . . . p u b l i c CartaDaGioco ( i n t s , i n t v ) { seme = s ; ... } Java 1.5 (Tiger) Tipi enumerati in Java pre-Tiger Alcuni problemi di questa soluzione: Il codice diventa criptico; per scrivere “se il seme è cuori o fiori” dovremmo scrivere qualcosa tipo: i f ( c . seme == 0 | | c . seme == 2 ) . . . Nessuno impedisce di mettere in seme qualcosa che non sia 0, 1, 2, 3 Java 1.5 (Tiger) Tipi enumerati in Java pre-Tiger In Java pre-Tiger per risolvere il primo dei due problemi si usavano le costanti: c l a s s CartaDaGioco { public f in a l static public f in a l static public f in a l static public f in a l static p u b l i c i n t seme ; . . . int int int int CUORI = 0 ; QUADRI = 1 ; FIORI = 2 ; PICCHE = 3 ; p u b l i c CartaDaGioco ( i n t s , i n t v ) { seme = s ; ... } Con questo, il codice diventa più leggibile: i f ( c . seme == C a r t a . CUORI | | c . seme == C a r t a . FIORI ) . . . Java 1.5 (Tiger) Tipi enumerati in Java pre-Tiger L’uso delle costanti non risolve però il problema di impedire che a seme si possano assegnare valori non consentiti (p.es., 8). Inoltre, c’è il problema (se non altro concettuale) di avere una variabile dichiarata di tipo intero, ma i cui valori non sono affatto interi (almeno idealmente). Per risolvere questi problemi, Tiger introduce una nuova categoria di tipi, detti tipi enumerati. Java 1.5 (Tiger) Tipi enumerati Un tipo enumerato, in Tiger, si dichiara come una classe, ma usando la parola chiave enum: enum Seme { CUORI , QUADRI , FIORI , PICCHE } A questo punto, si usano come se fossero nuovi tipi: c l a s s CartaDaGioco { p u b l i c Seme seme ; . . . p u b l i c C a r t a D a G i o c o ( Seme s , i n t v ) { seme = s ; ... } ... Java 1.5 (Tiger) Tipi enumerati Ecco ad esempio, come si crea una carta (sto assumendo di aver usato la stessa tecnica anche per i valori): C a r t a c = new C a r t a ( Seme . CUORI , V a l o r e .DONNA ) ; e: i f ( c . seme == Seme . CUORI | | c . seme == Seme . FIORI ) . . . Java 1.5 (Tiger) Tipi enumerati: vantaggi Un enum è un tipo riferimento; p.es., ha un metodo toString() che restituisce la stringa che uno si aspetta: cioè o u t . p r i n t l n ( c . seme ) ; stamperà CUORI, QUADRI, FIORI, PICCHE a seconda dei casi. Usare i tipi enumerati è più sicuro: nessuno può assegnare a seme un valore che non sia uno di quelli possibili. Java 1.5 (Tiger) Tipi enumerati: switch È possibile fare switch su valori enumerati (senza qualificarli) switch case case case case } ( c . seme ) { CUORI : . . . QUADRI : . . . PICCHE : . . . FIORI Java 1.5 (Tiger) Tipi enumerati: iterazione Il metodo statico values() restituisce un array contenente i possibili valori di un tipo enumerato; questo rende possibile iterare su valori enumerati f o r ( Seme seme : Seme . v a l u e s ( ) ) o u t . p r i n t l n ( seme ) ; Java 1.5 (Tiger) Tipi enumerati: di più I tipi enumerati sono un argomento molto più vasto; per esempio, è possibile creare insiemi di valori enumerati; creare mappe di valori enumerati; aggiungere metodi a un tipo enumerato; scrivere tipi enumerati che implementano interfacce; ... Al di fuori degli scopi di questa lezione. Java 1.5 (Tiger) Approfondimenti Lucidi e codice sorgente di questa lezione: http://boldi.dsi.unimi.it/Corsi/FAP2005/ Brett McLaughlin, David Flanagan: Java 1.5 Tiger — A Developer’s Notebook. O’Reilly, 2004. Sun Microsystems: The Java Language Specification, Release 1.5. Java 1.5 (Tiger)