Introduzione alle Classi Interne e alle Classi Annidate in Java
- Le classi interne e annidate in Java consentono di definire classi all'interno di altre classi, migliorando l'organizzazione del codice.
- Le classi annidate possono essere statiche o non statiche, con differenze nell'accesso ai membri della classe esterna.
- Le classi interne hanno accesso diretto ai membri della loro classe esterna, mentre le classi annidate statiche devono accedere ai membri non statici tramite un'istanza della classe esterna.
- Le classi annidate sono utili per gestire eventi e semplificare il codice, specialmente in contesti come la programmazione GUI.
- Le classi annidate locali possono essere definite all'interno di blocchi di codice, come metodi o cicli, e hanno accesso ai membri della classe esterna.
Classi Interne e Classi Annidate
È possibile definire una classe all'interno di un'altra classe; tali classi sono conosciute come classi annidate. L'ambito di una classe annidata è delimitato dall'ambito della sua classe contenitore.
Così, se la classe B è definita all'interno della classe A, allora B non esiste indipendentemente da A. Una classe annidata ha accesso ai membri, inclusi i membri privati, della classe in cui è annidata. Tuttavia, la classe contenitore non ha accesso ai membri della classe annidata. Una classe annidata che è dichiarata direttamente all'interno della sua classe contenitore è un membro della sua classe contenitore. È anche possibile dichiarare una classe annidata che è locale a un blocco.
Esistono due tipi di classi annidate: statiche e non statiche. Una classe annidata statica è una classe a cui è stato applicato il modificatore static
. Poiché è statica, deve accedere ai membri non statici della sua classe contenitore tramite un oggetto. Cioè, non può fare riferimento direttamente ai membri non statici della sua classe contenitore.
Il secondo tipo di classe annidata è la classe interna. Una classe interna è una classe annidata non statica. Ha accesso a tutte le variabili e ai metodi della sua classe esterna e può fare riferimento ad essi direttamente nello stesso modo in cui fanno altri membri non statici della classe esterna.
Il seguente programma illustra come definire e usare una classe interna. La classe chiamata Contenitore
ha una variabile di istanza chiamata contenitore_x
, un metodo di istanza chiamato verifica()
, e definisce una classe interna chiamata Interna
.
// Dimostrazione di una classe interna.
class Contenitore {
int contenitore_x = 100;
void verifica() {
Interna interna = new Interna();
interna.visualizza();
}
// questa è una classe interna
class Interna {
void visualizza() {
System.out.println("visualizza: contenitore_x = " + contenitore_x);
}
}
}
class DimostrazioneClasseInterna {
public static void main(String[] args) {
Contenitore contenitore = new Contenitore();
contenitore.verifica();
}
}
L'output di questa applicazione è il seguente:
visualizza: contenitore_x = 100
Nel programma, una classe interna chiamata Interna
è definita all'interno dell'ambito della classe Contenitore
.
Pertanto, qualsiasi codice nella classe Interna
può accedere direttamente alla variabile contenitore_x
. Un metodo di istanza chiamato visualizza()
è definito all'interno di Interna
. Questo metodo visualizza contenitore_x
sullo stream di output standard.
Il metodo main()
della classe DimostrazioneClasseInterna
crea un'istanza della classe Contenitore
e invoca il suo metodo verifica()
. Tale metodo crea un'istanza della classe Interna
e viene chiamato il metodo visualizza()
.
Creazione corretta di un'istanza della classe interna
È importante comprendere che un'istanza di Interna può essere creata solo nel contesto della classe Contenitore. Il compilatore Java genera un messaggio di errore altrimenti.
In generale, un'istanza della classe interna è spesso creata da codice all'interno del suo ambito contenitore, come nell'esempio.
Come spiegato, una classe interna ha accesso a tutti i membri della sua classe contenitore, ma non vale il contrario. I membri della classe interna sono noti solo all'interno dell'ambito della classe interna e non possono essere usati dalla classe esterna. Ad esempio:
// Questo programma non verrà compilato.
class Contenitore {
int contenitore_x = 100;
void verifica() {
Interna interna = new Interna();
interna.visualizza();
}
// questa è una classe interna
class Interna {
int y = 10; // y è locale a Interna
void visualizza() {
System.out.println("visualizza: contenitore_x = " + contenitore_x);
}
}
void mostra() {
System.out.println(y); // errore, y non è conosciuta qui!
}
}
class DimostrazioneClasseInterna {
public static void main(String[] args) {
Contenitore contenitore = new Contenitore();
contenitore.verifica();
}
}
Qui, y
è dichiarata come variabile di istanza di Interna
. Pertanto, non è conosciuta al di fuori di quella classe e non può essere usata da mostra()
.
Classi Interne Locali
Anche se finora ci si è concentrati su classi interne dichiarate come membri all'interno di un ambito di classe esterna, è possibile definire classi interne all'interno di qualsiasi ambito di blocco.
Ad esempio, è possibile definire una classe annidata all'interno di un blocco definito da un metodo o persino all'interno del corpo di un ciclo for
, come mostra il seguente programma:
// Definire una classe interna all'interno di un ciclo for.
class Contenitore {
int contenitore_x = 100;
void verifica() {
for(int i = 0; i < 10; i++) {
class Interna {
void visualizza() {
System.out.println("visualizza: contenitore_x = " + contenitore_x);
}
}
Interna interna = new Interna();
interna.visualizza();
}
}
}
class DimostrazioneClasseInterna {
public static void main(String[] args) {
Contenitore contenitore = new Contenitore();
contenitore.verifica();
}
}
L'output di questa versione del programma è il seguente:
visualizza: contenitore_x = 100
visualizza: contenitore_x = 100
visualizza: contenitore_x = 100
visualizza: contenitore_x = 100
visualizza: contenitore_x = 100
visualizza: contenitore_x = 100
visualizza: contenitore_x = 100
visualizza: contenitore_x = 100
visualizza: contenitore_x = 100
visualizza: contenitore_x = 100
Utilità delle classi annidate negli eventi
Sebbene le classi annidate non siano applicabili in tutte le situazioni, risultano particolarmente utili nella gestione degli eventi.
Il tema delle classi annidate verrà ripreso nelle future lezioni. Mostreremo come le classi interne possano essere utilizzate per semplificare il codice necessario a gestire determinati tipi di eventi.
Si tratterà anche delle classi interne anonime, che sono classi interne prive di nome.
Nota storica sulle classi annidate
Le classi annidate non erano consentite dalla specifica originale 1.0 di Java. Sono state aggiunte in Java 1.1.