Cuando se agregaron clases internas a Java en la versión 1.1 del lenguaje, originalmente se definieron como una transformación a un código compatible con 1.0. Si miras un ejemplo de esta transformación, creo que aclarará mucho cómo funciona realmente una clase interna.
Considere el código de la respuesta 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();
}
}
Cuando se transforma a un código compatible con 1.0, esa clase interna Barse convertiría en algo como esto:
class Foo$Bar {
private Foo this$0;
Foo$Bar(Foo outerThis) {
this.this$0 = outerThis;
}
public void printVal() {
System.out.println(this$0.val);
}
}
El nombre de la clase interna tiene como prefijo el nombre de la clase externa para que sea único. Se this$0agrega un miembro privado oculto que contiene una copia del externo this. Y se crea un constructor oculto para inicializar ese miembro.
Y si miras el createBarmétodo, se transformaría en algo como esto:
public Foo$Bar createBar() {
return new Foo$Bar(this);
}
Entonces, veamos qué sucede cuando ejecuta el siguiente código.
Foo f = new Foo(5);
Foo.Bar b = f.createBar();
b.printVal();
Primero instanciamos una instancia de Fooe inicializamos el valmiembro en 5 (es decir f.val = 5).
A continuación, llamamos f.createBar(), que instancia una instancia de Foo$Bare inicializa el this$0miembro con el valor de thispasado de createBar(es decir b.this$0 = f).
Finalmente llamamos a b.printVal()cuál intenta imprimir b.this$0.valcuál es f.valcuál es 5.
Ahora que era una instanciación regular de una clase interna. Veamos qué sucede cuando se crea una instancia Bardesde el exterior Foo.
Foo f = new Foo(5);
Foo.Bar b = f.new Bar();
b.printVal();
Aplicando nuestra transformación 1.0 nuevamente, esa segunda línea se convertiría en algo como esto:
Foo$Bar b = new Foo$Bar(f);
Esto es casi idéntico a la f.createBar()llamada. Nuevamente, estamos creando una instancia de Foo$Bare inicializando el this$0miembro en f. Así que de nuevo b.this$0 = f.
Y nuevamente cuando llamas b.printVal(), estás imprimiendo b.thi$0.valcuál es f.valcuál es 5.
La clave para recordar es que la clase interna tiene un miembro oculto que tiene una copia de thisla clase externa. Cuando crea una instancia de una clase interna desde dentro de la clase externa, se inicializa implícitamente con el valor actual de this. Cuando crea una instancia de la clase interna desde fuera de la clase externa, especifica explícitamente qué instancia de la clase externa usar, a través del prefijo de la newpalabra clave.