Gdy klasy wewnętrzne zostały dodane do języka Java w wersji 1.1 języka, zostały pierwotnie zdefiniowane jako transformacja do kodu zgodnego z wersją 1.0. Jeśli spojrzysz na przykład tej transformacji, myślę, że to znacznie wyjaśni, jak faktycznie działa klasa wewnętrzna.
Rozważ kod z odpowiedzi Iana Robertsa:
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();
}
}
Po przekształceniu do kodu kompatybilnego z 1.0, ta klasa wewnętrzna Barwyglądałaby mniej więcej tak:
class Foo$Bar {
private Foo this$0;
Foo$Bar(Foo outerThis) {
this.this$0 = outerThis;
}
public void printVal() {
System.out.println(this$0.val);
}
}
Nazwa klasy wewnętrznej jest poprzedzona nazwą klasy zewnętrznej, aby była unikalna. this$0Dodawany jest ukryty prywatny członek, który przechowuje kopię zewnętrznego this. I tworzony jest ukryty konstruktor w celu zainicjowania tego elementu członkowskiego.
A jeśli spojrzysz na createBarmetodę, zostanie przekształcony w coś takiego:
public Foo$Bar createBar() {
return new Foo$Bar(this);
}
Zobaczmy więc, co się stanie, gdy wykonasz następujący kod.
Foo f = new Foo(5);
Foo.Bar b = f.createBar();
b.printVal();
Najpierw tworzymy instancję Fooi inicjalizujemy element valczłonkowski do 5 (tj f.val = 5.).
Następnie wywołujemy f.createBar(), co tworzy instancję Foo$Bari inicjalizuje element this$0członkowski do wartości thisprzekazanej z createBar(tj b.this$0 = f.).
Wreszcie nazywamy b.printVal()który próbuje drukować b.this$0.val, który jest f.val, który jest 5.
To była zwykła instancja klasy wewnętrznej. Spójrzmy, co się dzieje podczas tworzenia instancji Barz zewnątrz Foo.
Foo f = new Foo(5);
Foo.Bar b = f.new Bar();
b.printVal();
Stosując ponownie naszą transformację 1.0, ta druga linia wyglądałaby mniej więcej tak:
Foo$Bar b = new Foo$Bar(f);
Jest to prawie identyczne z f.createBar()wezwaniem. Ponownie tworzymy wystąpienieFoo$Bar i inicjalizujemy element this$0członkowski f. Więc znowu b.this$0 = f.
I znowu, kiedy dzwonisz b.printVal(), drukujeszb.thi$0.val , który jest f.val, który jest 5.
Kluczową rzeczą do zapamiętania jest to, że klasa wewnętrzna ma ukrytego członka przechowującego kopię thisklasy zewnętrznej. Kiedy tworzysz wystąpienie klasy wewnętrznej z poziomu klasy zewnętrznej, jest ona niejawnie inicjowana z bieżącą wartością this. Podczas tworzenia wystąpienia klasy wewnętrznej spoza klasy zewnętrznej jawnie określasz, które wystąpienie klasy zewnętrznej ma być używane, za pomocą prefiksu newsłowa kluczowego.