MokaByte
Numero 29 - Aprile 1999
|
|||
|
|
III parte le inner classes
|
|
|
Massimo Carli |
- III parte - |
|
Riprendiamo il corso sulle Swing che ha subito un rallentamento di un mese a causa dell'uscita dell'atteso JDK1.2 in versione definitiva che d'ora in poi utilizzeremo nei nostri esempi. Il cambiamento della versione del JDK annunciata nella prima puntata del corso non porterà comunque a grossi problemi in quanto abbiamo appena accennato ad alcune delle classi presenti in esso. Da questo mese, dopo un piccolo-grande sforzo inizieremo finalmente a vedere le Swing all'opera. L'obiettivo di questa puntata del corso è quello di creare una semplice applicazione costituita da un piccolo JFrame che contiene alcuni bottoni che ci permetteranno di modificarne il L&F. Prima di fare questo introdurremo due concetti fondamentali nella programmazione Swing e non solo: le Inner Classes, il Modello per Delega (Delegation Model). Consiglio al lettore di approfondire molto bene questi argomenti per avere notevoli benefici non solo nell'utilizzo delle Swing ma anche nella programmazione Java in generale. In questa puntata del corso vedremo, inoltre, come l'utilizzo congiunto delle Inner Classes e del Delegation Model, permetta di scrivere codice di buona leggibilità in poco tempo. |
Le
Inner Classes
Le Inner Classes (Classi Interne) sono state introdotte dal JDK1.1 e rappresentano un insieme di nuove regole per la scrittura di classi ed interfacce. Nella scrittura del codice ci si è resi conto della mancanza di uno strumento che permettesse la creazione di determinate classi con visibilità limitata a quella di una classe iniziale, che condividesse con essa alcuni dati o che semplicemente fosse legata alla prima da un insieme di considerazioni logiche. Ci si è accorti che sarebbe stato molto utile poter definire una classe all'interno di un'altra in modo da condividerne tutte le proprietà senza dover creare classi con costruttori o altri metodi set enormi con grandissima possibilità di errore. Il problema si traduceva, quindi, nella realizzazione di un particolare compilatore che incontrate queste classi interne (Inner Classes), le traducesse in un modo comprensibile all'interprete. Quest'ultimo, infatti, non chiede altro che disporre di tanti file .class quante sono le classi con cui deve avere a che fare. Da queste considerazioni si evince che le Inner Classes sono una nuova potenzialità del compilatore e che, almeno in questo contesto, non si hanno problemi di compatibilità con le altre versioni della JVM. Possiamo classificare
le Inner Classes in quattro gruppi diversi:
Classi ed Interfacce Interne Top-Level Questa categoria di classi interne è rappresentata da un insieme di classi ed interfacce che non hanno caratteristiche differenti dalle classi che le contengono se non per il fatto di essere legate ad esse da una relazione logica di convenienza come può essere, ad esempio, quella di appartenere ad uno stesso package o di riguardare uno stesso aspetto della programmazione (I/O, networking, ecc). Queste classi interne sono anche dette statiche in quanto ci si riferisce ad esse utilizzando il nome della classe che le contiene allo stesso modo di una proprietà statica e la relativa classe. Per comprendere meglio questo tipo di Inner Classes esaminiamo nel dettaglio l'esempio dato dal Listato3_1. Ogni riga del listato è stata numerata in modo da semplificarne la spiegazione. L'esempio rappresenta la realizzazione di un contenitore di un certo tipo di oggetti Item le cui caratteristiche sono descritte dalla omonima interfaccia. Tali oggetti devono possedere una chiave ed un valore entrambi di tipo String (ovviamente si tratta di un esempio e non si fanno considerazioni sulla effettiva utilità di questa classe anche se si noteranno le analogie con uno stack). L'interfaccia Item è legata alla realizzazione della classe ItemContainer per cui sarebbe utile un procedimento che le legasse in qualche modo. Analoga considerazione si può fare per la classe SimpleItem che rappresenta la più semplice implementazione dell'interfaccia Item. Nel JDK1.02 avremmo potuto organizzare queste classi ed interfacce mettendole tutte in uno stesso package. Esiste, però, anche una gerarchia logica tra la classe ItemContainer e le altre. Le Inner Classes ci permettono di definire l'interfaccia Item e la classe SimpleItem all'interno della definizione della classe ItemContainer e di riferirci ad esse come ItemContainer.Item ed ItemContainer.SimpleItem. La stessa cosa può essere fatta per le eventuali eccezioni sollevate da alcuni metodi. Nel Listato 3_1 notiamo la definizione dell'interfaccia Item alla riga 31. Notiamo come essa sia definita come una qualunque altra interfaccia solo che è all'interno della definizione di una classe. Alla riga 58 inizia la definizione della classe SimpleItem che implementa l'interfaccia Item definita precedentemente. E' possibile riferirsi all'interfaccia semplicemente con il suo nome in quanto siamo all'interno della classe SimpleItem. In caso contrario avremmo dovuto riferirci ad essa come ItemContainer.Item. Nella definizione della classe interna notiamo anche l'utilizzo del modificatore static. Questo modificatore permette di indicare che la classe SimpleItem è da considerarsi una classe come le altre solamente che per riferirsi ad essa bisogna passare per la classe ItemContainer. Il legame tra le classi è quindi solamente logico ed una istruzione del tipo: ItemContainer.SimpleItem
item= new ItemContainer.SimpleItem();
è perfettamente
lecita. Nel caso dell'interfaccia non è necessario inserire il modificatore
static in quanto non ha senso creare una istanza di una interfaccia. Ogni
interfaccia è quindi sempre statica. Le considerazioni fatte per
la classe SimpleItem valgono, ovviamente, anche per le eccezioni FullItemContainerException
e EmptyItemContainerException introdotte qui per completezza.
1: package corso.swing.capitolo3; 2: 3: 4: /** 5: *Verifica della classe <EM>corso.swing.capitolo3.ItemContainer</EM> 6: * 7: *@author Massimo Carli 8: *@version 0.1 (28/12/1998) 9: */ 10: 11: 12: public class ProvaItemContainer { 13: 14: 15: /* 16: *Main 17: */ 18: public static void main(String str[]){ 19: ItemContainer container=new ItemContainer(); 20: try{ 21: container.addItem("1","uno"); 22: container.addItem("2","due"); 23: container.addItem("3","tre"); 24: }catch(ItemContainer.FullItemContainerException e){ 25: System.out.println("Errore: "+e.getMessage()); 26: }// fine try/catch 27: 28: try{ 29: /* 30: * Se proviamo ad estrarre subito un oggetto di tipo Item, 31: * ovvero che implementa l'omonima interfaccia, nel modo 32: * che segue (commentato) si avrebbe un errore in quanto 33: * l'inteprete non riesce a trovare tale tipo. 34: */ 35: 36: //Item item=container.removeItem(); // Errato 37: 38: /* 39: * Il modo seguente è quello corrretto ovvero l'interfaccia 40: * Item e' dichiarata come "contenuta" nella classe 41: * ItemContainer e viene indicata come suo membro statico. 42: * Questo e' il motivo per cui le classi interne di questo 43: * tipo sono dette STATICHE 44: */ 45: 46: ItemContainer.Item item=container.removeItem(); // Esatto 47: 48: /* 49: * Se non si vuole definire l'interfaccia Item nel modo 50: * visto, sarebbe sufficiente importarla nel seguente 51: * modo: 52: * 53: * import corso.swing.capitolo3.ItemContainer.*; 54: * 55: * ovvero valgono le stesse regole relative alla 56: * organizzazione di classi ed interfaccie in package. 57: * Considerazione analoga per le eccezioni. 58: */ 59: 60: System.out.println("key : "+item.getKey()+" value= "+item.getValue()); 61: 62: }catch(ItemContainer.EmptyItemContainerException e){ 63: System.out.println("Errore: "+e.getMessage()); 64: }// fine try/catch 65: }// fine 66: 67: 68: 69: }// fine classe ProvaItemContainer
Abbiamo detto
che la possibilità di creare classi innestate una dentro l'altra
doveva essere gestita dal compilatore. Se compiliamo la classe ItemContainer
notiamo, oltre alla creazione del file ItemContainer.class, la creazione
dei file ItemContainer$Item.class, ItemContainer$SimpleItem.class, ItemContainer$EmptyItemContainerException
e ItemContainer$FullItemContainerException. Notiamo inoltre che questi
file sono contenuti nella stessa directory per cui non si ha, nella organizzazione
fisica delle classi, un procedimento analogo a quello dei package. Il compilatore
ha eseguito quella che si chiama source-code transformation inserendo il
simbolo $ per indicare che una classe è contenuta in un'altra.ù
Classi Membro Le classi top-level descritte in precedenza sono dette anche statiche in quanto non sono legate ad una particolare istanza della classe che le contiene ma da un legame logico. Le Member Classes (Classi Membro) sono definite allo stesso modo delle classi top-level con l'importante differenza di non essere definite con il modificatore static. Questo ha conseguenza importantissime. Ogni istanza di una Member Classes è associata ad una particolare istanza della classe che le contiene. Inoltre, ogni classe membro può accedere a qualunque proprietà o metodo (anche definiti private), della classe, o della gerarchia di classi, che la contengono. Non solo. Anche le classi che la contengono possono accedere a proprietà e metodi delle classi membro (in questo caso assume importanza il modificatore di acceso utilizzato nella definizione della classe interna). Ecco che le classi membro vengono utilizzate come classi di supporto che devono accedere ad un certo insieme di informazioni della classe che le contiene. Come esempio supponiamo di aggiungere alla classe ItemContainer il metodo elements() che fornisce una Enumeration dei dati in esso contenuti. Ricordiamo che per Enumeration intendiamo una qualunque classe che implementa l'interfaccia java.util.Enumeration. Se avessimo voluto procedere nel modo classico avremmo creato una nuova classe ItemEnumerator contenuta in un omonimo file. Questa classe avrebbe avuto un costruttore, o un metodo set, per ricevere il riferimento all'ItemContainer (o semplicemente alla sua proprietà items) necessario per accedere agli Item e creare l'Enumeration. Questo, come detto, ci obbligherebbe alla creazione di un nuovo file, di una nuova classe ma, soprattutto, a decidere come fornire alla nuova classe il riferimento all'ItemContainer. Nel nostro caso la cosa non sarebbe difficile ma non sono rari i casi in cui questo procedimento può portare a facili errori. Nel Listato 3_3 diamo la soluzione utilizzando le Member Classes. 1: package corso.swing.capitolo3; 2: 3: import java.util.*; 4: 5: /** 6: *Questa classe aggiunge il metodo <EM>elements()</EM> che permette 7: *di ottenere una Enumeration degli elementi contenuti in 8: *un <EM>ItemContainer</EM>. 9: * 10: *@author Massimo Carli 11: *@version 0.1 (03/01/1999) 12: */ 13: 14: public class EnumeratedItemContainer extends ItemContainer{ 15: 16: /** 17: * In questo punto definiamo la classe membro 18: *<EM>ItemEnumerator</EM> che ha visibilità su 19: * tutte le proprietà della classe <EM> ItemContainer </EM> 20: */ 21: private class ItemEnumerator implements Enumeration { 22: 23: /* 24: *Posizione dell'elemento corrente nell'array 25: *<EM> items</EM> 26: */ 27: private int indice_corrente; 28: 29: /** 30: *Costruttore: notiamo che non necessita di 31: *alcun parametro in quanto vede tutte le 32: *proprietà della classe che la contiene. 33: */ 34: public ItemEnumerator(){ 35: indice_corrente=0; 36: }// fine 37: 38: /** 39: *Primo metodo dell'interfaccia <EM>Enumeration</EM> 40: *Indica se esistono elementi 41: */ 42: public boolean hasMoreElements(){ 43: return (indice_corrente<size); 44: }// fine 45: 46: 47: /** 48: *Secondo metodo dell'interfaccia <EM>Enumeration</EM> 49: *Ritorna l'elemento corrente 50: */ 51: public Object nextElement(){ 52: if(indice_corrente>size) 53: throw new NoSuchElementException("Item Finiti"); 54: Object obj=items[indice_corrente++]; 55: return obj; 56: }// fine 57: }// fine classe membro 58: 59: 60: /** 61: *Costruttore di default 62: */ 63: public EnumeratedItemContainer(){ 64: super(); 65: }// fine 66: 67: /** 68: *Crea un contenitore di dimensione massima <EM>size</EM> 69: *e che utilizza un <EM>SimpleItem</EM> come factory 70: *delle Item 71: *@param dim Dimensione massima del contenitore 72: */ 73: public EnumeratedItemContainer(int dim){ 74: super(dim); 75: }// fine 76: 77: /** 78: *Crea un contenitore di dimensione massima <EM>size</EM> 79: *e che utilizza builder come factory delle Item 80: *@param dim Dimensione massima del contenitore 81: *@param factory Factory degli Item 82: */ 83: public EnumeratedItemContainer(int dim,Item factory){ 84: super(dim,factory); 85: }// fine 86: 87: 88: /** 89: *Ritorna una <EM>Enumeration</EM> degli item 90: */ 91: public Enumeration elements(){ 92: /** 93: *Creiamo una instanza della classe <EM>ItemEnumerator</EM> 94: *la quale utilizza le proprietà ed i valori della particolare 95: *instanza della classe <EM>ItemContainer</EM> cui essa appartiene. 96: */ 97: return new ItemEnumerator(); 98: }// fine 99: 100: }//fine classe 101:
La nuova classe EnumeratedItemContainer si ottiene estendendo la classe ItemContainer e aggiungendo il metodo elements() alla riga 91. Notiamo come esso non faccia altro che ritornare una istanza della classe ItemEnumerator. Il costruttore invocato non ha nessun parametro. La definizione della classe ItemEnumerator è alla linea 21. Notiamo che non compare il modificatore static per cui la classe definita è una classe membro. Essa ha accesso a tutte le proprietà della classe in cui è contenuta. Questo è evidente alla riga 54 in cui, all'interno della classe ItemEnumerator, accediamo alla proprietà items della classe in cui è contenuta. Il valore di tale proprietà è quello relativo ad una particolare istanza della classe EnumeratedItemContainer ovvero a quella si cui si invoca il metodo elements(). 1: package corso.swing.capitolo3; 2: 3: import java.util.*; 4: 5: /** 6: *Verifica della classe <EM>corso.swing.capitolo3.EnumeratedItemContainer</EM> 7: * 8: *@author Massimo Carli 9: *@version 0.1 (02/01/1999) 10: */ 11: 12: 13: public class ProvaEnumeratedItemContainer { 14: 15: 16: /* 17: *Main 18: */ 19: public static void main(String str[]){ 20: EnumeratedItemContainer container=new EnumeratedItemContainer(); 21: try{ 22: container.addItem("1","uno"); 23: container.addItem("2","due"); 24: container.addItem("3","tre"); 25: }catch(ItemContainer.FullItemContainerException e){ 26: System.out.println("Errore: "+e.getMessage()); 27: }// fine try/catch 28: Enumeration enum= container.elements(); 29: while(enum.hasMoreElements()){ 30: ItemContainer.Item item=(ItemContainer.Item)(enum.nextElement()); 31: System.out.println("key:"+item.getKey()+"value="+item.getValue()); 32: }// fine while 33: }// fine main 34: 35: 36: 37: }// fine classe ProvaItemContainer
Il Listato3_4
è un esempio di utilizzo della classe EnumeratedItemContainer. In
tale esempio si richiama semplicemente il metodo elements() sulla istanza
container della classe EnumeratedItemContainer (riga 28). Gli elementi
della Enumeration saranno quelli contenuti nella particolare istanza container.
1: 2: 3: - - - - 4: 5: /** 6: * In questo punto definiamo la classe membro 7: *<EM>ItemEnumerator</EM> che ha visibilità su 8: * tutte le proprietà della classe <EM> ItemContainer </EM> 9: */ 10: private class ItemEnumerator implements Enumeration { 11: 12: 13: /* 14: *Array che contiene gli Item 15: */ 16: protected Item[] items; 17: 18: 19: /** 20: *Costruttore: notiamo che non necessita di 21: *alcun parametro in quanto vede tutte le 22: *proprietà della classe che la contiene. 23: */ 24: public ItemEnumerator(){ 25: 26: /* 27: *1) Questo metodo e' errato perche' this.items e items sono 28: * la stessa cosa in questo metodo. 29: */ 30: 31: this.items=items; // NO 32: 33: /* 34: *2) Questo procedimento permette di riferirsi ad una 35: * proprietà specificando il tipo della istanza a 36: * cui appartiene 37: */ 38: 39: this.items= EnumeratedItemContainer.this.items; 40: }// fine 41: 42: 43: 44: - - - -
All'interno del
costruttore della ItemEnumerator avremmo potuto assegnare il valore della
proprietà items della classe contenitore alla omonima proprietà
della classe interna. Ma come facciamo a distinguere le due proprietà
items? Il metodo classico sarebbe quello indicato nella riga 31. In questo
caso si avrebbero dei problemi in quanto items e this.items indicano la
stessa proprietà in quanto siano interni alla definizione della
classe ItemEnumerator. Dal JDK1.1 è stata allora introdotta la notazione
nomeclasse.this per indicare in modo univoco, la classe a cui la parola
chiave this si riferisce. Nel nostro caso con EnumeratedItemContainer.this.items
indichiamo la proprietà items della classe EnumeratedItemContainer.
Questo metodo funziona anche se la classe EnumeratedItemContainer non fosse
la classe che contiene ItemEnumerator direttamente, ma attraverso
più livelli.
istanza.new Classe();
per creare una
istanza della classe interna Classe riferendoci alla istanza istanza della
classe contenitore. Nel nostro caso avremmo dovuto agire come nel Listato3_6
in cui, alla riga 22, creiamo l'istanza della classe ItemEnumerator riferita
alla particolare istanza container della classe EnumeratedItemContainer.
1: 2: 3: - - - - 4: 5: 6: /** 7: *Main di prova 8: */ 9: public static void main(String[] str){ 10: 11: - - - - 12: /* 13: *Creiamo una istanza della classe EnumeratedItemContainer 14: */ 15: EnumeratedItemContainer container=new EnumeratedItemContainer(); 16: 17: 18: /* 19: * Creiamo un ItemEnumerator che utilizzerà i dati della istanza 20: * appena creata. 21: */ 22: Enumeration enum=container.new ItemEnumerator(); 23: 24: - - - - 25: 26: }// fine main 27: 28: 29: 30: - - - -
Una considerazione
analoga può essere fatta per la parola chiave super. Da un punto
di vista puramente teorico potrebbe capitare il caso in cui una classe
top-level derivi da una classe membro. Questo significa che una sottoclasse
che non è contenuta in nessun'altra classe, ha una superclasse che
lo è. Nel caso in cui nel costruttore della classe derivata si richiamasse
il costruttore della superclasse, attraverso super(), bisognerà
specificare rispetto a quel istanza tale costruttore deve riferirsi. In
tale caso si utilizza una notazione analoga a quella del this ovvero quella
del Listato3_7.
1: package corso.swing.capitolo3; 2: 3: 4: 5: /** 6: * Esempio di una classe top-level che estende una classe interna. 7: * 8: *@author Massimo Carli 9: *@version 0.1 (03/01/1999) 10: */ 11: 12: public class ClasseTopLevel extends EnumeratedItemContainer.ItemEnumerator { 13: 14: 15: /** 16: *Costruttore di default. 17: */ 18: public ClasseTopLevel(EnumeratedItemContainer eic){ 19: iec.super(); 20: }// fine 21: 22: - - - 23: 24: }// fine classe
Alla riga 18
vi è la definizione del costruttore che possiede un parametro di
tipo EnumeratedItemContainer che corrisponde alla particolare istanza
di cui richiamare il costruttore con super(). Nel Listato3_7 notiamo che
anche una classe membro viene indicata con la notazione utilizzata per
le classi top-level. Nel nostro caso si ha, infatti, la classe EnumeratedItemContainer.ItemEnumerator.
Classi Locali Con le classi locali (Local Classes) iniziamo finalmente la trattazione delle classi che si utilizzano maggiormente nelle Swing per la gestione degli eventi con il metodo per delega (Delegation Model). Le classi locali, a differenza delle classi membro a cui assomigliano, hanno la caratteristica di essere definite all'interno di un blocco di codice Java. Esse hanno visibilità legata a quella del blocco Java (ovvero parte compresa tra { e } ) che le contengono. Questo porta ad una importante conseguenza: le classi locali possono accedere, oltre ai metodi e proprietà delle classi che la contengono, anche alle sole proprietà final definite nello stesso blocco. Questo è causa della source-code transformation operata dal compilatore. Quando viene creata una istanza di una classe locale, i valori delle variabili visibili in tale scopo, vengono copiate in altrettante variabili della inner classes. Per assicurare la coerenza tra i valori definiti nello scopo in cui è definita la classe locale e quello interni alla classe stessa, l'unico modo era quello di considerare solamente le proprietà final. La definizione di una proprietà final in un determinato blocco di codice è stata introdotta nel JDK 1.1. proprio per questo aspetto. Le limitazioni della classi locali sono quelle di non poter utilizzare proprietà static e di non poter essere dichiarate come public, protected, private o static. Quest'ultima considerazione è forse la principale distinzione tra una classe interna ed una classe locale. Come semplice
esempio di questo tipo di classe interna estendiamo la classe EnumeratedItemContainer
aggiungendo il metodo getExtremes() che ritorna un oggetto di tipo ExtremeItems,
che dispone semplicemente di due proprietà first e last che contengono
gli Item estremi.
1: package corso.swing.capitolo3; 2: 3: import java.util.*; 4: 5: /** 6: *Questa classe estende <EM>EnumeratedItemContainer</EM> e 7: *mostra un semplice utilizzo delle classi Locali. 8: * 9: *@author Massimo Carli 10: *@version 0.1 (24/01/1999) 11: */ 12: 13: public class EnumeratedItemContainerWithLocal 14: extends EnumeratedItemContainer{ 15: 16: 17: 18: 19: /** 20: *Questa interfaccia è necessaria per poter indicare il tipo 21: *di ritorno della classe locale ExtremeItems. 22: */ 23: public interface ExtremeInterface { 24: 25: /** 26: *Ritorna il primo Item 27: */ 28: public Item getFirst(); 29: 30: /** 31: *Ritorna l'ultimo Item 32: */ 33: public Item getLast(); 34: 35: }// fine interfacciainterna 36: 37: 38: 39: /** 40: *Costruttore di default. Definiamo solo questo 41: *per motivi di spazio. 42: */ 43: public EnumeratedItemContainerWithLocal(){ 44: super(); 45: }// fine 46: 47: 48: /** 49: *Ritorna una Enumeration che contiene solamente il primo 50: *e l'ultimo Item, ovviamente se presenti 51: */ 52: public ExtremeInterface getExtremes(){ 53: 54: /* 55: *Classe interna che ritorna un oggetto che contiene gli oggetti 56: *estremi contenuti 57: * 58: */ 59: class ExtremeItems implements ExtremeInterface{ 60: 61: /* 62: *Indice del primo 63: */ 64: public Item first; 65: 66: /* 67: *Indice dell'ultimo elemento 68: */ 69: public Item last; 70: 71: 72: /** 73: *Costruttore: notiamo che non necessita di 74: *alcun parametro in quanto vede tutte le 75: *proprietà della classe che la contiene. 76: */ 77: public ExtremeItems(){ 78: if(size==0) 79: first=last=null; 80: else { 81: first=items[0]; 82: last=items[size-1]; 83: }// fine else 84: }// fine 85: 86: 87: 88: /** 89: *Ritorna il primo elemento 90: */ 91: public Item getFirst(){ 92: return first; 93: }// fine metodo 94: 95: /** 96: *Ritorna l'ultimo elemento 97: */ 98: public Item getLast(){ 99: return last; 100: }// fine metodo 101: 102: 103: }// fine definizione classe interna 104: 105: return new ExtremeItems(); 106: }// fine metodo 107: 108: 109: 110: 111: 112: }// fine classe EnumeratedItemContainerWithLocal
Alla riga 23
abbiamo dichiarato una interfaccia che ci permette di indicare il tipo
dell'oggetto di ritorno del metodo getExtremes().Non avremmo potuto, infatti,
indicare come tipo dell'oggetto di ritorno quello dichiarato all'interno
del metodo stesso. Alla riga 52 inizia la definizione del metodo getExtremes().
All'interno del corpo del metodo vi è quindi la definizione della
classe ExtremeItems. Notiamo come l'intestazione della classe non presenti
alcun modificatore di accesso. Tale classe, infatti, ha visibilità
e vita legati solamente a quelli dello scopo in cui è dichiarata
ed un modificatore di accesso non avrebbe senso. Tutte le proprietà
utilizzate dalla classe locale sono visibili al di fuori dello scopo in
cui essa è definita per cui non ci sono problemi legati all'utilizzo
del modificatore final. Sarà il compilatore che si preoccuperà
di passare, attraverso il costruttore, un riferimento a tali proprietà.
Nel caso in cui ci fossero state delle variabili definite all'interno del
metodo getExtremes(), esse avrebbero dovuto essere definite final.
Classi Anonime L'ultimo tipo
di inner classes che esaminiamo è quello delle classi anonime o
anonymous classes. Esse si possono tranquillamente considerare delle classi
locali prive di nome e, a differenza di queste, vengono definite in una
espressione invece che in uno statement Java. Dalla versione JDK1.1 è
stata introdotta una nuova sintassi per l'utilizzo di questo tipo di classe.
Nel caso in cui si volesse creare una istanza anonima di una classe nome_classe
basterà scrivere:
new nome_classe ([lista-parametri]) { // corpo della classe }
Con questa sintassi
si crea una istanza di una classe anonima che estende la classe di nome
nome_classe. Nel corpo della classe anonima andranno ridefiniti alcuni
metodi della classe nome_classe implicitamente estesa, o definiti nuovi
metodi di utilità. Siccome una classe anonima non ha nome si presenta
il problema della creazione di un costruttore. Esistono quindi due possibilità.
new Panel(nuovo_layout){ public paint(Graphics g){ // nuovo corpo della paint }// fine paint }// fine anonymous class
Si utilizza un'altra
nuova funzionalità introdotta nel JDK1.1 chiamata instance initializer.
Esso non è altro che un blocco{} inserito nel corpo della classe.
Esso verrà eseguito alla creazione di ogni istanza della classe
stessa.
new Panel(){ // Instance initializer { setLayout(nuovo_layout); } public paint(Graphics g){ // nuovo corpo della paint }// fine paint }// fine anonymous class
Per le anonymous
classes esiste anche la seguente nuova sintassi:
new nome_interfaccia (){ // corpo della classe }
In questo caso
nome_interfaccia è una interfaccia ed il corpo della classe ne deve
rappresentare una implementazione. Nel corpo della classe dovranno essere
implementati, quindi, tutti i metodi della interfaccia dichiarata dopo
il new. Nel caso di una Enumeration basterebbe scrivere:
new Enumeration(){ public boolean hasMoreElements(){ // implementazione }// fine public Object nextElement(){ // implementazione }// fine }// fine
L'utilizzo delle
classi anonime è molto semplice e velocizza di molto la scrittura
del codice. Ovviamente il suo utilizzo è consigliato quando si ha
a che fare con implementazioni e classi molto piccole quali quelle di gestione
degli eventi che vedremo in seguito.
|
|
||
|
||
MokaByte ricerca
nuovi collaboratori
|
||
|