1

Initially I thought I would have a chicken and egg problem here, but exploring this in a unit test doesn't indicate any problems. I want to understand what is going on here. I would have thought that since enums are static and final the MyEnum constructor would run when the JVM loads MyClass. However in my test it prints "getValue" before "MyEnum constructor".

MyClass {

   private enum MyEnum {
       VALUE;

       MyEnum() {
          System.out.println("MyEnum constructor");
          MyClass clazz = new MyClass(); 
       }
   }

   public MyEnum getValue() {
      System.out.println("getValue");
      return MyEnum.VALUE;
   }

}

public class MyClassTest {

    @Test
    public void testStuff() {
        MyClass clazz = new MyClass();
        clazz.getValue();
    }
}
hvgotcodes
  • 118,147
  • 33
  • 203
  • 236
  • Can you clarify what you think _static initialization time_ is? – Savior Sep 14 '18 at 16:41
  • Potential duplicate https://stackoverflow.com/questions/28296547/execution-order-of-enum-in-java – Savior Sep 14 '18 at 16:42
  • Or https://stackoverflow.com/questions/3818026/force-initialization-of-an-enumerated-type-in-java – Savior Sep 14 '18 at 16:42
  • I meant when the class is loaded. I don't think this is a duplicate, this has a private enum inside of a class whereas the proposed dupe does not. – hvgotcodes Sep 14 '18 at 16:43
  • The location of the `MyEnum` type is not important. It will be initialized, like any class, according to https://stackoverflow.com/questions/3499214/when-does-static-class-initialization-happen, ie. _a non-constant static field is used_. That field is the enum constant. – Savior Sep 14 '18 at 16:55
  • My example shows that the the print in the `getValue` method is firing BEFORE the enum constructor, which seems opposite of what your link says – hvgotcodes Sep 14 '18 at 17:02
  • The `MyEnum.VALUE` enum constant is accessed inside `getValue` after the print. So the `MyEnum` class is only initialized _then_. – Savior Sep 14 '18 at 17:08

1 Answers1

1

The fact that a class/interface/enum/annotation is nested within another class does not affect when its initialization will occur.

The rules for initialization apply regardless. They are defined in the JLS here.

A class or interface type T will be initialized immediately before the first occurrence of any one of the following:

  • T is a class and an instance of T is created.
  • A static method declared by T is invoked.
  • A static field declared by T is assigned.
  • A static field declared by T is used and the field is not a constant variable (§4.12.4).

The JLS also says the following about enum types

An enum declaration specifies a new enum type, a special kind of class type.

And about its members, it says

For each enum constant c declared in the body of the declaration of E, E has an implicitly declared public static final field of type E that has the same name as c. The field has a variable initializer which instantiates E and passes any arguments of c to the constructor chosen for E. The field has the same annotations as c (if any).

Put all this together and you get an explanation for the behavior you see.

Your code instantiates MyClass, then invokes its getValue() method. getValue() prints something to standard out and then tries to access a static field declared by MyEnum. This triggers initialization of the enum type, which initializes the public static static VALUE field, which invokes the corresponding MyEnum construct that again prints to standard out.

Savior
  • 3,225
  • 4
  • 24
  • 48