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 ) であるためであることに注意してください。このテスト ハーネスでは:value
Name
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 にラップされます)。