匿名クラスの処理に直面した奇妙な動作を理解したいと思います。
コンストラクター内で保護されたメソッドを呼び出すクラスがあります (設計が悪いことはわかっていますが、それは別の話です...)
public class A {
public A() {
init();
}
protected void init() {}
}
次に、拡張Aおよびオーバーライドする別のクラスがありますinit()。
public class B extends A {
int value;
public B(int i) {
value = i;
}
protected void init() {
System.out.println("value="+value);
}
}
私がコーディングする場合
B b = new B(10);
私は得る
> value=0
スーパークラスのコンストラクターはBctorの前に呼び出され、その後も呼び出されるため、これは予想されvalueます。
しかし、このような匿名クラスを使用する場合
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); }
}
}
}
value=0これは class と多かれ少なかれ等しいはずなので、得られると期待していますB。コンパイラは、匿名クラスのメソッドで参照されるローカル変数を格納するインスタンス変数をC$1拡張して作成する新しいクラスを自動的に作成し、クロージャなどをシミュレートします...A
しかし、これを実行すると、
> java -cp . C 42
> value=42
当初、これは私がJava 8を使用していたことが原因であると考えていました.lamdbasを導入したときに、ボンネットの下で匿名クラスが実装される方法が変更された可能性があります(必要なくなりましたfinal)が、Java 7で試しましたまた、同じ結果を得ました...
実際、 でバイトコードをjavap見るとB、
> 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
...
間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
....
誰かがこの違いの理由を教えてもらえますか? 「通常の」クラスを使用して匿名クラスの動作を複製する方法はありますか?
EDIT:質問を明確にするために:匿名クラスの初期化が他のクラスの初期化のルールを破るのはなぜですか(他の変数を設定する前にスーパーコンストラクターが呼び出されます)?Bまたは、スーパー コンストラクターを inovking する前にクラスでインスタンス変数を設定する方法はありますか?