Realisierung innerer Klassen
Innere Klassen - Die Magie dahinter
- Innere Klassen sind reine Compile-Zeit-Konstrukte
- In der Laufzeit (Java VM) existieren keine inneren Klassen
- Der Compiler erzeugt besonderen Code, um innere Klassen auf normale top-level Klassen abzubilden
- synthetische Zugriffsmethoden
- synthetische Felder für die this-Referenzen
- spezielle Konstruktoren
- Die inneren Klassen bekommen besondere Namen
Outer$Inner
für benannte innere KlassenOuter$1
,Outer$2
etc. für anonyme innere Klassen
Man hat bei der Einführung der inneren Klassen davon abgesehen, die Virtual-Machine zu verändern und hat sie daher vollständig auf den Compiler abgestützt. Der Grund war wohl, dass man ansonsten die Kompatibilität zu allen existierenden VMs zerstört hätte und erhebliche Aufwände bei den VM-Anbietern entstanden wären. Diesen Ansatz wählt man bei Java relativ häufig, sodass andere Konstrukte wie z. B. die foreach-Schleife oder Enumerationen ohne Änderung der VM-Spezifikation eingeführt wurden.
Wegen der generierten synthetischen Methoden sind innere Klassen mit einem gewissen Geschwindigkeitsnachteil belegt, da ein einfacher Feldzugriff zum Aufruf einer statischen Methode werden kann. Moderne VMs optimieren zwar solche Konstrukte recht aggressiv (Inlining), trotzdem kann es vorkommen, dass die Geschwindigkeit leidet. In solchen Fällen sollte man die Methoden der inneren Klasse statt private einfach mit default-Sichtbarkeit versehen, da dann keine synthetischen Zugriffsmethoden mehr erzeugt werden müssen.
Beispiel: Source Code
public class A {
private int i; private int j;
public class B {
private int i; private int j;
private void methodB(String s) {
i = A.this.i + A.this.j + j;
methodA("Welt");
}
}
private int methodA(String s) {
B b = new B();
b.i = 5;
b.methodB("Hallo");
return b.i + b.j;
}
}
Beispiel: Echter Code für Klasse A
public class A {
private int i;
private int j;
public A() {}
private int methodA(String s) {
A$B b = new A$B(this); // B b = new B();
A$B.access$0(b, 5); // b.i = 5;
A$B.access$1(b, "Hallo"); // b.methodB("Hallo");
return A$B.access$2(b) + A$B.access$3(b); // return b.i + b.j;
}
static int access$0(A a) { return a.i; }
static int access$1(A a) { return a.j; }
static int access$2(A a, String s) { return a.methodA(s); }
}
Beispiel: Echter Code für Klasse A$B
public class A$B {
private int i;
private int j;
private void methodB(String s) {
// i = A.this.i + A.this.j + j;
i = A.access$0(this$0) + A.access$1(this$0) + j;
A.access$2(this$0, "Welt"); // methodA("Welt");
}
public A$B(A a) {
this$0 = a;
}
final A this$0;
static void access$0(A$B b, int i) { b.i = i; }
static void access$1(A$B b, String s) { b.methodB(s); }
static int access$2(A$B b) { return b.i; }
static int access$3(A$B b) { return b.j; }
}