3

final staticJava 列挙型クラス内で、クラスの を含む配列を作成したいと思いますvalues()。次の行に沿ってこれを行うと、結果の配列はnull.

public enum Name {
    E1( stuff ), E2( stuff );
    private static final Name[] values = Name.values();

    private Name( stuff ) { more stuff; }
}

また、明示的なクラス セッター メソッドを呼び出してこれを実行しようとしましたが、java.lang.ExceptionInInitializerError例外が発生しました。

stuff前のコードでは、列挙されたクラスに依存する他のクラスを使用しているため、問題がいくつかの浅い依存関係によって引き起こされていることを理解しています。

私が必要とするものを達成するためのテスト済みで実証済みの技術はありますか?

4

2 に答える 2

3

tl;dr:あなたがしようとしていることは不可能です - 列挙型の静的フィールドは、すべてのコンストラクター呼び出しが完了するまで初期化されません。


次の例を検討してください。

public enum Name {
  E1("hello"), E2("world");

  private static final Name[] values = values();

  private Name(String val) {
    System.out.println("val = " + val);
    dump();
  }

  protected void dump() {
    System.out.println("this = " + this + ", values = " + values);
  }
}

メソッドが存在する理由は、のコンストラクター内からフィールドを参照しようとdumpするのがコンパイル時エラー ( Java 言語仕様セクション 8.9.2 ) であるためであることに注意してください。このテスト ハーネスでは:valueName

public class Main {
  public static void main(String... args) throws Exception {
    System.out.println(Name.values());
  }
}

我々が得る

$ java Main
val = hello
this = E1, values = null
val = world
this = E2, values = null
[LName;@35960f05

Nameでクラスを逆コンパイルするとjavap、次のようになります。

private static final Name[] $VALUES;

public static Name[] values();
  Code:
   0:   getstatic   #1; //Field $VALUES:[LName;
   3:   invokevirtual   #2; //Method "[LName;".clone:()Ljava/lang/Object;
   6:   checkcast   #3; //class "[LName;"
   9:   areturn

$VALUESコンパイラは、値の配列を保持するプライベート フィールドを作成し、values()メソッドは として実装され{ return (Name[])$VALUES.clone() }ます。では、どのよう$VALUESに初期化されるのでしょうか?

static {};
  Code:
   0:   new #4; //class Name
   3:   dup
   4:   ldc #19; //String E1
   6:   iconst_0
   7:   ldc #20; //String hello
   9:   invokespecial   #21; //Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
   12:  putstatic   #22; //Field E1:LName;
   15:  new #4; //class Name
   18:  dup
   19:  ldc #23; //String E2
   21:  iconst_1
   22:  ldc #24; //String world
   24:  invokespecial   #21; //Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
   27:  putstatic   #25; //Field E2:LName;
   30:  iconst_2
   31:  anewarray   #4; //class Name
   34:  dup
   35:  iconst_0
   36:  getstatic   #22; //Field E1:LName;
   39:  aastore
   40:  dup
   41:  iconst_1
   42:  getstatic   #25; //Field E2:LName;
   45:  aastore
   46:  putstatic   #1; //Field $VALUES:[LName;
   49:  invokestatic    #26; //Method values:()[LName;
   52:  putstatic   #18; //Field values:[LName;
   55:  return

}

ここでわかるのは、初期化が本質的に行うことです。

// compiler-generated initialization code
E1 = new Name("hello");
E2 = new Name("world");
$VALUES = new Name[] {E1, E2};

// static initializer of the values field
values = Name.values();

そのため、コンストラクター呼び出しの実行中、valuesフィールドは null になり、values()メソッドは NullPointerException をスローします (これは ExceptionInInitializerError にラップされます)。

于 2012-09-16T15:32:05.430 に答える
2

null であってはならないため、これが発生する例を挙げていただけますか。

public class Main {
    public enum Name {
        E1(  ), E2(  );
        private static final Name[] VALUES = Name.values();
    }


    public static void main(String... args) {
        System.out.println(Name.VALUES);
        System.out.println(Arrays.asList(Name.VALUES));
    }
}

版画

[LMain$Name;@717e5fde
[E1, E2]
于 2012-09-16T14:15:46.350 に答える