使用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.ExceptionInInitializerError
exception。
我理解这个问题是由一些浅的依赖引起的,因为前面代码中的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]