Quando le classi interne sono state aggiunte a Java nella versione 1.1 del linguaggio, sono state originariamente definite come una trasformazione in codice compatibile 1.0. Se guardi un esempio di questa trasformazione, penso che renderà molto più chiaro come funziona effettivamente una classe interna.
Considera il codice dalla risposta di Ian Roberts:
public class Foo {
int val;
public Foo(int v) { val = v; }
class Bar {
public void printVal() {
System.out.println(val);
}
}
public Bar createBar() {
return new Bar();
}
}
Quando trasformata in codice compatibile 1.0, quella classe interna Bardiventerebbe qualcosa del genere:
class Foo$Bar {
private Foo this$0;
Foo$Bar(Foo outerThis) {
this.this$0 = outerThis;
}
public void printVal() {
System.out.println(this$0.val);
}
}
Il nome della classe interna è preceduto dal nome della classe esterna in modo da renderlo univoco. Viene this$0aggiunto un membro privato nascosto che contiene una copia dell'esterno this. E un costruttore nascosto viene creato per inizializzare quel membro.
E se guardi il createBarmetodo, si trasformerebbe in qualcosa del genere:
public Foo$Bar createBar() {
return new Foo$Bar(this);
}
Quindi vediamo cosa succede quando esegui il codice seguente.
Foo f = new Foo(5);
Foo.Bar b = f.createBar();
b.printVal();
Per prima cosa istanziamo un'istanza di Fooe inizializziamo il valmembro su 5 (cioè f.val = 5).
Successivamente chiamiamo f.createBar(), che istanzia un'istanza di Foo$Bare inizializza il this$0membro al valore di thispassato da createBar(ie b.this$0 = f).
Infine chiamiamo b.printVal()quale prova a stampare b.this$0.valquale è f.valquale è 5.
Questa era un'istanza regolare di una classe interna. Diamo un'occhiata a cosa succede quando si crea un'istanza Bardall'esterno Foo.
Foo f = new Foo(5);
Foo.Bar b = f.new Bar();
b.printVal();
Applicando di nuovo la nostra trasformazione 1.0, quella seconda riga diventerebbe qualcosa del genere:
Foo$Bar b = new Foo$Bar(f);
Questo è quasi identico alla f.createBar()chiamata. Anche in questo caso stiamo creando un'istanza di Foo$Bare inizializzando il this$0membro su f. Quindi, di nuovo, b.this$0 = f.
E di nuovo quando si chiama b.printVal(), si stampa b.thi$0.val, che è f.val, che è 5.
La cosa fondamentale da ricordare è che la classe interna ha un membro nascosto che contiene una copia di thisdalla classe esterna. Quando si istanzia una classe interna dall'interno della classe esterna, questa viene inizializzata implicitamente con il valore corrente di this. Quando si istanzia la classe interna dall'esterno della classe esterna, si specifica esplicitamente quale istanza della classe esterna utilizzare, tramite il prefisso sulla newparola chiave.