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)