Quando as classes internas foram adicionadas ao Java na versão 1.1 da linguagem, elas foram originalmente definidas como uma transformação para o código compatível com 1.0. Se você olhar um exemplo dessa transformação, acho que ficará muito mais claro como uma classe interna realmente funciona.
Considere o código da resposta de 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 transformada em código compatível com 1.0, essa classe interna Barse tornaria algo assim:
class Foo$Bar {
private Foo this$0;
Foo$Bar(Foo outerThis) {
this.this$0 = outerThis;
}
public void printVal() {
System.out.println(this$0.val);
}
}
O nome da classe interna é prefixado com o nome da classe externa para torná-lo único. Um this$0membro privado oculto é adicionado, contendo uma cópia dothis . E um construtor oculto é criado para inicializar esse membro.
E se você olhar para o createBarmétodo, ele seria transformado em algo assim:
public Foo$Bar createBar() {
return new Foo$Bar(this);
}
Então, vamos ver o que acontece quando você executa o código a seguir.
Foo f = new Foo(5);
Foo.Bar b = f.createBar();
b.printVal();
Primeiro, instanciamos uma instância de Fooe inicializamos o valmembro em 5 (ou sejaf.val = 5 ).
Em seguida, chamamos f.createBar(), que instancia uma instância de Foo$Bare inicializa o this$0membro com o valor de thispassado de createBar(ou seja,b.this$0 = f ).
Finalmente chamamos o b.printVal()que tenta imprimir o b.this$0.valque éf.val que é 5.
Essa foi uma instanciação regular de uma classe interna. Vejamos o que acontece ao instanciar Barde fora Foo.
Foo f = new Foo(5);
Foo.Bar b = f.new Bar();
b.printVal();
Aplicando nossa transformação 1.0 novamente, a segunda linha se tornaria algo assim:
Foo$Bar b = new Foo$Bar(f);
Isso é quase idêntico à f.createBar()chamada. Novamente, estamos instanciando uma instância de Foo$Bare inicializando o this$0membro para f. Então novamente,b.this$0 = f ,.
E novamente quando você ligar b.printVal(), você está imprimindo b.thi$0.valo que é f.valque é 5.
O principal a ser lembrado é que a classe interna tem um membro oculto que contém uma cópia da thisclasse externa. Quando você instancia uma classe interna de dentro da classe externa, ela é inicializada implicitamente com o valor atual de this. Ao instanciar a classe interna de fora da classe externa, você especifica explicitamente qual instância da classe externa usar, por meio do prefixo da newpalavra - chave.