
"Ciao, Amico!"
"Ma ci siamo già salutati, Ellie!"
"Ehi, non discutere con tua zia. Nel 31° secolo, se non vedi qualcuno da più di mezz'ora, è consuetudine salutare di nuovo. Quindi non darmi il tuo atteggiamento!"
"Comunque, è il momento di un altro argomento interessante: la riproduzione dei robot!"
"O_O."
"Sto scherzando, il nuovo argomento sono le classi interne anonime ."
"In Java, a volte ci sono situazioni in cui avrai bisogno di una classe per ereditare diverse classi. Poiché Java non supporta l'ereditarietà multipla, hanno risolto questo problema usando le classi interne: nella nostra classe, dichiariamo una classe interna e creiamo eredita qualunque classe ci serva per ereditare. Ecco un esempio:"
class Tiger extends Cat
{
public void tigerRun()
{
.....
}
public void startTiger()
{
TigerThread thread = new TigerThread();
thread.start();
}
class TigerThread extends Thread
{
public void run()
{
tigerRun();
}
}
}
"Facciamo un altro esempio:"
Abbiamo bisogno di una sottoclasse della classe Thread per sovrascrivere il suo metodo run."
"Ecco perché nella classe Tiger abbiamo dichiarato la classe interna TigerThread , che eredita Thread e sovrascrive il metodo run.
"Per comodità, abbiamo definito due metodi nella classe Tiger: tigerRun e startTiger (che sono analoghi ai metodi run e start di Thread."
"Nel metodo tigerStart, creiamo un oggetto TigerThread e invochiamo il suo metodo start()."
"La JVM creerà un nuovo thread che inizierà a funzionare quando viene chiamato il metodo run di TigerThread ."
"Questo metodo chiama quindi il nostro metodo run : tigerRun ."
"Ho già lavorato con i thread, quindi sembra semplice."
"Dobbiamo nominare i metodi tigerRun e tigerStart?"
"No, avremmo potuto chiamarli corri e inizia, ma volevo anche dimostrare che non stiamo ereditando Thread. Una spiegazione avrebbe potuto essere più confusa."
"OK. Allora penso di aver capito. Ma se tigerStart viene chiamato una seconda volta, creeremo e avvieremo un secondo oggetto Thread. Non significa che avremo «una tigre in esecuzione su due thread diversi»? "
"Beh, non sei intelligente! Hai ragione, e non va bene. Riscriviamo il codice in questo modo:"
class Tiger extends Cat
{
public void tigerRun()
{
.....
}
public void startTiger()
{
thread.start();
}
private TigerThread thread = new TigerThread();
private class TigerThread extends Thread
{
public void run()
{
tigerRun();
}
}
}
"Non è del tutto perfetto. Non puoi ancora chiamare un metodo del genere due volte. Ma questa volta, almeno non creeremo un secondo thread e faremo sembrare che tutto vada bene."
"Esatto. La seconda volta che viene avviato un Tiger, avrai un'eccezione."
"Sto già individuando gli errori meglio di te, Ellie!"
"Sì, stai andando alla grande. Allora passiamo alle classi interne anonime."
"Nota diversi aspetti del codice sopra:"
1) Abbiamo ereditato la classe Thread, ma praticamente non abbiamo implementato alcun codice. "Era più «abbiamo dovuto ereditare la classe Thread» piuttosto che «l'abbiamo ereditata per estenderla».
2) Verrà creato un solo oggetto TigerThread.
In altre parole, abbiamo scritto un sacco di codice solo per sovrascrivere un metodo e creare un oggetto.
Ricordi come ho parlato dell'invenzione dei costruttori?
Prima dei costruttori | Dopo i costruttori |
---|---|
|
|
"Vedo che il codice è diventato più compatto, ma non capisco bene cosa stia succedendo."
"Possiamo combinare quattro cose in una:"
1) dichiarazione di una classe derivata
2) sostituzione del metodo
3) dichiarazione di una variabile
4) creazione di un'istanza di una classe derivata.
"In effetti, quello che stiamo facendo è combinare due operazioni: dichiarare una classe derivata e creare un'istanza di quella classe."
Senza classe anonima | Con classe anonima |
---|---|
|
|
"Esploriamo di nuovo la sintassi:"
Thread thread = new Thread();
Thread thread = new Thread()
{
};
"Nota che non stiamo semplicemente definendo una nuova classe. Stiamo creando una variabile—c'è un punto e virgola alla fine!"
"E se vogliamo sovrascrivere il metodo run, allora dobbiamo scrivere questo:"
Thread thread = new Thread()
{
public void run()
{
System.out.println("new run-method");
}
};
"Hai capito in fretta. Ben fatto!"
"Grazie. E se avessimo bisogno di altri metodi che non fanno parte della classe Thread?"
"Puoi scriverli."
"Sebbene anonima, questa è una classe interna a tutti gli effetti:"
codice java | Descrizione |
---|---|
|
Rosso: codice per la creazione della variabile.
Verde: codice per la creazione dell'oggetto. Blu: codice per la classe derivata anonima. |
"Una classe interna a tutti gli effetti?"
"Quindi posso usare le variabili della classe esterna?"
"Assolutamente."
"E posso passare qualcosa al costruttore?"
"Sì, ma solo gli argomenti per il costruttore della superclasse:"
Classe | Istanza di una classe interna anonima |
---|---|
|
|
"Non possiamo aggiungere i nostri parametri al costruttore di qualcun altro. Ma possiamo usare le variabili della classe esterna, che compensa bene questa mancanza."
"E se avessi ancora davvero bisogno di aggiungere altri parametri al costruttore?"
"Quindi dichiara una classe interna ordinaria (non anonima) e usala."
"Giusto, me ne ero quasi dimenticato."
"E se dichiaro una variabile statica? Ciò renderebbe la classe anonima una classe nidificata statica piuttosto che una classe interna? In altre parole, mancherebbe un riferimento alla classe esterna?"
"No. Sarebbe una classe interna anonima. Guarda questi esempi."
Con classe anonima | Senza classe anonima |
---|---|
|
|
|
|
"Capisco. Solo la variabile statica sarebbe statica, non la classe."
"Sì."
"In effetti, il compilatore crea classi interne per tutte le classi interne anonime. Queste classi sono solitamente denominate «1», «2», «3», ecc."