使用值()创建枚举常量的最终Java类数组 [英] Creating a final Java class array of enum constants with values( )

查看:137
本文介绍了使用值()创建枚举常量的最终Java类数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

私人名称(stuff){更多东西; }
}



我也尝试通过调用显式类setter方法,但是这给出了 java.lang.ExceptionInInitializerError 异常。



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



tl; dr:你想要的是什么?这样做是不可能的 - 枚举类型的静态字段在所有构造函数调用完成后才会初始化。






请考虑这个例子:

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

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);
}
}

请注意, c $ c> 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






使用 javap 解析 Name 类,我们看到以下内容:

  private static final Name [] $ VALUES; 

public static Name [] values();
代码:
0:getstatic#1; // Field $ VALUES:[LName;
3:invokevirtual#2; //方法[LName;。clone :()Ljava / lang / Object;
6:checkcast#3; // class[LName;
9:areturn

编译器创建一个私有字段 $ VALUES 方法实现为 {return(Name [])$ VALUES。 clone()} 。那么如何 $ VALUES 得到初始化?

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

}

初始化基本上是:

  //编译器生成的初始化代码
E1 = new Name(hello) ;
E2 = new Name(world);
$ VALUES = new Name [] {E1,E2};

//值字段的静态初始值设定项
values = Name.values();

因此在执行构造函数调用期间, values 字段将为null,并且 values()方法将抛出NullPointerException(将被包装在ExceptionInInitializerError中)。


Inside a Java enumerated class, I'd like to create a final static array containing the values() of the class. When I do this along the following lines, the resulting array is null.

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

    private Name( stuff ) { more stuff; }
}

I've also tried doing this by calling an explicit class setter method, but this gave an java.lang.ExceptionInInitializerError exception.

I understand the problem is caused by some shallow dependencies as the stuff in the previous code uses other classes, which themselves depend on the enumerated class.

Is there a tested and proven technique to achieve what I need?

解决方案

tl;dr: what you're trying to do isn't possible - static fields of an enum type don't get initialized until after all the constructor calls have completed.


Consider this example:

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);
  }
}

Note that the reason for the existence of the dump method is that it is a compile-time error (Java Language Spec section 8.9.2) to try and reference the value field from inside the constructor of Name. With this test harness:

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

we get

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


Decompiling the Name class with javap we see the following:

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

The compiler creates a private field $VALUES holding the value array, and the values() method is implemented as { return (Name[])$VALUES.clone() }. So how does $VALUES get initialized?

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

}

What we see here is that the initialization essentially does:

// 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();

so during the execution of the constructor calls, the values field will be null and the values() method will throw a NullPointerException (which will get wrapped in an ExceptionInInitializerError).

这篇关于使用值()创建枚举常量的最终Java类数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆