Creating a final Java class array of enum constants with values( )

class, constructor, enums, java

Solution

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).

Problem

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?

Original source