Ich würde gerne ein seltsames Verhalten verstehen, das ich mit anonymen Klassen konfrontiert.Java: Initialisierung und Costruktor von anonymen Klassen
Ich habe eine Klasse, die eine geschützte Methode in seinem Konstruktor aufruft (ich weiß, schlechtes Design, aber das ist eine andere Geschichte ...)
public class A {
public A() {
init();
}
protected void init() {}
}
dann habe ich eine andere Klasse, die A
und überschreibt init()
erstreckt.
public class B extends A {
int value;
public B(int i) {
value = i;
}
protected void init() {
System.out.println("value="+value);
}
}
Wenn ich Code
B b = new B(10);
ich
> value=0
und dass erwartet wird, weil der Konstruktor der Superklasse vor dem Ctor B
aufgerufen wird und dann noch value
ist.
Aber wenn eine anonyme Klasse wie dieser erwarten würde ich
class C {
public static void main (String[] args) {
final int avalue = Integer.parsetInt(args[0]);
A a = new A() {
void init() { System.out.println("value="+avalue); }
}
}
}
mit value=0
zu bekommen, weil dies sollte B
mehr oder weniger gleich Klasse sein: der Compiler erstellt automatisch eine neue Klasse C$1
die A
erweitert und schafft Instanzvariablen in dem Verfahren der anonymen Klasse verwiesen lokale Variablen zu speichern, einen Verschluss zu simulieren etc ...
Aber wenn man diese laufen, ich habe
> java -cp . C 42
> value=42
Zuerst dachte ich, dass dies auf die Tatsache zurückzuführen war, dass ich Java 8 verwendete, und vielleicht, als ich lamdbas einführte, änderten sie die Art und Weise wie anonyme Klassen unter der Haube implementiert werden (du brauchst nicht mehr final
), aber ich versuchte, mit Java 7 auch und bekam das gleiche Ergebnis ...
Eigentlich mit javap
auf Byte-Code suchen, kann ich sehen, dass B
ist
> javap -c B
Compiled from "B.java"
public class B extends A {
int value;
public B(int);
Code:
0: aload_0
1: invokespecial #1 // Method A."<init>":()V
4: aload_0
5: iload_1
6: putfield #2 // Field value:I
9: return
...
während für C$1
:
> javap -c C\$1
Compiled from "C.java"
final class C$1 extends A {
final int val$v;
C$1(int);
Code:
0: aload_0
1: iload_1
2: putfield #1 // Field val$v:I
5: aload_0
6: invokespecial #2 // Method A."<init>":()V
9: return
....
Kann mir jemand sagen, warum dieser Unterschied? Gibt es eine Möglichkeit, das Verhalten der anonymen Klasse mit "normalen" Klassen zu replizieren?
EDIT: , um die Frage zu klären: Warum bricht die Initialisierung der anonymen Klassen die Regeln für die Initialisierung einer anderen Klasse (wo Superkonstruktor aufgerufen wird, bevor Sie eine andere Variable setzen)? Oder gibt es eine Möglichkeit, Instanzvariable in B
Klasse vor Inovking Superkonstruktor festlegen?
Warum denken Sie, dass Ihr erster und zweiter Code gleich ist? Im zweiten Code greifen Sie auf die lokale Variable zu. Das wird initialisiert, bevor Ihre anonyme Klassenanweisung ausgeführt wird. –
umh ... ok, du sagst: Die Tatsache, dass der Compiler eine Klasse zur Implementierung dieses Szenarios erstellt, sollte dem Entwickler verborgen bleiben, also ist die 'C $ 1' Klasse ein Sonderfall, und es ist in Ordnung, wenn sie nicht dem Standard entspricht Konstruktorregeln. Das ist ziemlich vernünftig, aber trotzdem, es ist ein bisschen peinlich. – ugo