Bei anonymen Klassen deklarieren Sie tatsächlich eine "namenlose" verschachtelte Klasse. Für verschachtelte Klassen generiert der Compiler eine neue eigenständige öffentliche Klasse mit einem Konstruktor, der alle von ihm verwendeten Variablen als Argumente verwendet (für "benannte" verschachtelte Klassen ist dies immer eine Instanz der ursprünglichen / umschließenden Klasse). Dies geschieht, weil die Laufzeitumgebung keine Vorstellung von verschachtelten Klassen hat. Daher muss eine (automatische) Konvertierung von einer verschachtelten in eine eigenständige Klasse erfolgen.
Nehmen Sie diesen Code zum Beispiel:
public class EnclosingClass {
public void someMethod() {
String shared = "hello";
new Thread() {
public void run() {
// this is not valid, won't compile
System.out.println(shared); // this instance expects shared to point to the reference where the String object "hello" lives in heap
}
}.start();
// change the reference 'shared' points to, with a new value
shared = "other hello";
System.out.println(shared);
}
}
Das wird nicht funktionieren, denn das macht der Compiler unter der Haube:
public void someMethod() {
String shared = "hello";
new EnclosingClass$1(shared).start();
// change the reference 'shared' points to, with a new value
shared = "other hello";
System.out.println(shared);
}
Die ursprüngliche anonyme Klasse wird durch eine eigenständige Klasse ersetzt, die der Compiler generiert (Code ist nicht genau, sollte Ihnen aber eine gute Idee geben):
public class EnclosingClass$1 extends Thread {
String shared;
public EnclosingClass$1(String shared) {
this.shared = shared;
}
public void run() {
System.out.println(shared);
}
}
Wie Sie sehen können, enthält die eigenständige Klasse einen Verweis auf das gemeinsam genutzte Objekt. Denken Sie daran, dass alles in Java als Wert übergeben wird. Selbst wenn die Referenzvariable 'shared' in EnclosingClass geändert wird, wird die Instanz, auf die sie verweist, nicht geändert und alle anderen Referenzvariablen, die darauf verweisen (wie die in der anonymen Klasse: Enclosing $ 1), werden dies nicht bemerken. Dies ist der Hauptgrund, warum der Compiler Sie zwingt, diese "gemeinsam genutzten" Variablen als endgültig zu deklarieren, damit diese Art von Verhalten nicht in Ihren bereits ausgeführten Code gelangt.
Dies geschieht nun, wenn Sie eine Instanzvariable in einer anonymen Klasse verwenden (dies sollten Sie tun, um Ihr Problem zu lösen, Ihre Logik auf eine "Instanz" -Methode oder einen Konstruktor einer Klasse zu verschieben):
public class EnclosingClass {
String shared = "hello";
public void someMethod() {
new Thread() {
public void run() {
System.out.println(shared); // this is perfectly valid
}
}.start();
// change the reference 'shared' points to, with a new value
shared = "other hello";
System.out.println(shared);
}
}
Dies lässt sich gut kompilieren, da der Compiler den Code so ändert, dass die neu generierte Klasse Enclosing $ 1 einen Verweis auf die Instanz von EnclosingClass enthält, in der sie instanziiert wurde (dies ist nur eine Darstellung, sollte Sie aber zum Laufen bringen):
public void someMethod() {
new EnclosingClass$1(this).start();
// change the reference 'shared' points to, with a new value
shared = "other hello";
System.out.println(shared);
}
public class EnclosingClass$1 extends Thread {
EnclosingClass enclosing;
public EnclosingClass$1(EnclosingClass enclosing) {
this.enclosing = enclosing;
}
public void run() {
System.out.println(enclosing.shared);
}
}
Wenn die Referenzvariable 'shared' in EnclosingClass neu zugewiesen wird und dies vor dem Aufruf von Thread # run () geschieht, wird "other hello" zweimal gedruckt, da jetzt die EnclosingClass $ 1 # -Einschließungsvariable eine Referenz behält für das Objekt der Klasse, in der es deklariert wurde, sodass Änderungen an einem Attribut für dieses Objekt für Instanzen von EnclosingClass $ 1 sichtbar sind.
Weitere Informationen zu diesem Thema finden Sie in diesem hervorragenden Blog-Beitrag (nicht von mir verfasst): http://kevinboone.net/java_inner.html