4

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?

8
  • is that all your enum definition? It doesn't have constructor that takes stuff? Could you paste the exception stack trace if possible? Commented Sep 16, 2012 at 14:12
  • 3
    Why do you need a variable? Can't you just call Name.values wherever you would use this variable? Commented Sep 16, 2012 at 14:15
  • 1
    @JohnWatts Name.values( ) returns a defensive copy. I want to avoid the overhead of creating the copy. Commented Sep 16, 2012 at 14:18
  • 1
    @JohnWatts seconded - this looks very much like premature optimisation. Commented Sep 16, 2012 at 14:19
  • @Vikdor I've added the ``constructor.'' Commented Sep 16, 2012 at 14:19

2 Answers 2

4

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

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for explaining this. I'm still not certain if it explains everything because I'm not referencing the array in the constructor of the enum members. The error happens quite late in the execution of my application.
You said that "the stuff in the previous code uses other classes, which themselves depend on the enumerated class", which sounds like you're calling something from the Name constructor which in turn is calling back into the enum. Essentially the answer is that you will always get null for the values field if the Name constructor is anywhere on your current call stack.
No. This is not the case. When I know what causes the problem, I'll get back and explain.
Hi again. It turned out there was a ``recursive'' call to the enum constructors. I've eliminated the problem and now (surprise, surprise) using the class array now also works. I should have spotted this problem....
2

Can you provide an example where this happens because it shouldn't be 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));
    }
}

prints

[LMain$Name;@717e5fde
[E1, E2]

3 Comments

Inmy case values( ) does return null. If I add an assertion in a class method in the enumerated class along the following lines I get an assertion error: assert( values != null ).
Can you provide a self contained example which does this? Can you run the example I gave to see if it happens? BTW Which version of Java are you using?
your example works (of course). As I explained in my question, there are some complications in my code because of some class dependencies. I haven't been able to create a minimal example.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.