5

匿名クラスの処理に直面した奇妙な動作を理解したいと思います。

コンストラクター内で保護されたメソッドを呼び出すクラスがあります (設計が悪いことはわかっていますが、それは別の話です...)

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 する前にクラスでインスタンス変数を設定する方法はありますか?

4

4 に答える 4

2

匿名クラス インスタンスが作成される前に値が初期化されるローカル変数を使用しているため、匿名クラス インスタンスは最初のコード スニペットとは異なる動作をします。

匿名クラスでインスタンス変数を使用すると、匿名クラス インスタンスで最初のスニペットと同様の動作を得ることができます。

class C {
  public static void main (String[] args) {
    A a = new A() {
      int avalue = 10;
      void init() { System.out.println("value="+avalue); }
    }
  }
}

これは印刷されます

value=0

が初期化される前に のコンストラクターinit()によって実行されるためです。Aavalue

于 2015-09-08T17:25:23.440 に答える
0

2 つの例は関連していません。

B の例では:

protected void init() {
    System.out.println("value="+value);
}

出力される値は、valueB のインスタンスのフィールドです。

匿名の例では:

final int avalue = Integer.parsetInt(args[0]);
A a = new A() {
    void init() { System.out.println("value="+avalue); }
}

出力される値avaluemain()メソッドのローカル変数です。

于 2015-09-08T18:51:05.323 に答える