![]() ![]() |
|
![]() ![]() |
I tipi generici forniscono uno strumento di
controllo statico molto espressivo e estremamente sofisticato,
difficilmente trattabile in modo esauriente in un corso del primo
anno. In questo modulo cercheremo di evidenziare solo alcune
caratteristiche principali, necessarie per interpretare correttamente
le API di Java 5.0 (e versioni successive).
Un esempio è la classe Vector che abbiamo già introdotto. Confrontiamo l'uso dei vettori in Java 5.0 e in Java 1.4.
Si osservi che:
|
![]() ![]() |
|
![]() ![]() |
La seguente classe Coppia ha due parametri di
tipo, E e F, e le sue istanze sono
coppie di oggetti. Si noti in particolare
l'uso delle variabili di tipo nel corpo della classe.
Istanze di questa classe possono essere utili se, ad esempio, un metodo deve restituire due valori o se dobbiamo gestire dei record di valori. Per comodità, non dovendo fornire garanzie sui dati incapsulati, dichiariamo le variabili d'istanza pubbliche (invece che private).
Si noti che il nome del file .java e del costruttore della classe generica sono semplicemente Coppia e non Coppia<E,F>. Notare anche che quando si dichiarano più parametri formali questi vengono tutti racchiusi tra una sola coppia di parentesi angolari e separati da virgole.
|
![]() ![]() |
|
![]() ![]() |
Una volta definita la classe Coppia<E,F> generica,
possiamo sfruttarla per rappresentare altre strutture: è
sufficiente istanziare opportunamente i parametri formali di tipo.
|
![]() ![]() |
|
![]() ![]() |
![]() ![]() |
|
![]() ![]() |
Un'utile estensione della classe Coppia può essere quella
di fornire un ordinamento lessicografico sulle coppie: prima si
confrontano gli elementi nella prima posizione e solo se questi sono
uguali si confrontano quelli in seconda posizione. Come esempi di uso
si pensi all'ordinamento di nominativi (cognome, nome), oppure di ore espresse come coppia (hh,mm), o di date espresse come terna (aaaa,(mm,gg)).
Abbiamo visto che il modo standard di definire il confronto è quello di implementare l'interfaccia Comparable. Quindi, analogamente alla classe Mela, l'intestazione della nostra classe dovrebbe essere qualcosa come:
Però se vogliamo poter confrontare le proiezioni della coppia separatamente, bisogna anche informare il compilatore che tali elementi sono a loro volta Comparable. Vediamo una realizzazione della classe CoppiaComp.
Si noti l'uso della parola chiave extends per dichiarare che E e F sono Comparable.
Negli esempi sopra sono stati omessi i costruttori. |
![]() ![]() |
|
![]() ![]() |
L'esempio riportato sotto mostra come sia possibile ordinare array di
coppie confrontabili con il metodo statico sort definito in java.utils.Arrays. Per garantire
un controllo forte dei tipi, Java
5.0 non permette di creare array il cui tipo base sia generico. È
però possibile creare array il cui tipo base estenda un tipo
generico, istanziandolo.
Esempio: CartaComp1, CartaComp2 e TestCarte.
Attenzione: il seguente metodo non compilerebbe:
|
![]() ![]() |
|
![]() ![]() |
Si provi a eseguire la classe seguente, prestando attenzione
all'output:
Esiste una sola classe, chiamata Coppia! (Indipendentemente da come vengono istanziati i suoi parametri di tipo). I parametri di tipo sono solo annotazioni per il compilatore che può così effettuare dei controlli ferrei sulla compatibilità degli oggetti e delle loro funzionalità. La compilazione trasforma gli oggetti generici nei corrispondenti oggetti non generici. A tempo di esecuzione i tipi generici non sono più disponibili. Questo meccanismo è chiamato erasure. Per garantire la compatibilità col codice Java 1.4 è possibile usare tipi generici senza istanziarne i parametri, anche se questo non sarebbe appropriato per Java 5.0. Ad esempio possiamo definire e usare dati di tipo Vector, in questo caso si parla di raw type. L'uso di raw type, sconsigliato ma consentito, può facilmente generare dei warning in fase di compilazione. ATTENZIONE: usare un raw type è diverso da istanziare i parametri con la superclasse Object (es. Vector e Vector<Object> non sono intercambiabili)! |
![]() ![]() |
|
![]() ![]() |
Analogamente a classi e interfacce
generiche, in Java 5.0 è possibile definire metodi generici, ovvero parametrici rispetto ad
uno o più tipi.
Esempio: MaxGenerico.
Nell'esempio:
Nota: quando si invoca un metodo generico non si devono specificare i parametri di tipo, infatti il compilatore inferisce questi parametri (nel modo più specifico possibile) sulla base degli argomenti attuali della chiamata.
|
![]() ![]() |
|
![]() ![]() |
In alcuni casi il parametro di tipo potrebbe essere dichiarato ma mai
usato (ad esempio se fosse utilizzato col solo scopo di restringere
gli argomenti che possono essere passati al metodo sfruttando il
controllo statico del compilatore). Invece di introdurre un parametro
con nome, possiamo usare la wildcard ?.
Esempio: Anagrafe.
Nell'esempio:
A completamento dell'esempio, menzioniamo che, analogamente alla dichiarazione E extends Persona che restringe il parametro di tipo E alle sottoclassi di Persona, è anche possibile restringere il parametro di tipo alle superclassi di una data classe: in questo caso la sintassi è E super Persona. |
![]() ![]() |
|
![]() ![]() |
Per concludere la panoramica sui tipi
generici evidenziamo alcuni aspetti riguardanti la relazione di
ereditarietà che potrebbero risultare controintuitivi:
|