使用values()创建枚举常量的最终Java类数组

在Java枚举类中,我想创建一个包含类的values()final static数组。 当我沿着以下行执行此操作时,结果数组为null

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

我也试过通过调用显式类setter方法来做到这一点,但这给出了java.lang.ExceptionInInitializerErrorexception。

我理解这个问题是由一些浅的依赖引起的,因为前面代码中的stuff使用其他类,它们本身依赖于枚举类。

是否有经过测试和validation的技术来实现我的需求?

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方法的原因是尝试从Name的构造函数内部引用value字段是编译时错误( Java语言规范部分8.9.2 )。 有了这个测试工具:

 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 

使用javap反编译Name类,我们看到以下内容:

 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 "":(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 "":(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中)。

你能提供一个例子,因为它不应该是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]