Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this enum code an illegal reference to a static field?

Tags:

java

enums

This code won't compile because there is an illegal reference to a static field.

public enum Foo {

  A,
  B;

  private Foo[] foos = new Foo[] { Foo.A };

}

Shouldn't you be able to access static fields from a non-static field initializer? For example:

public class Foo {

  static int A;

  private int[] foos = new int[] { Foo.A };

}

This compiles fine.

Note, making foos static in the first example compiles.

like image 557
Dave Avatar asked Feb 01 '12 15:02

Dave


People also ask

Can enum have static fields?

The enum class body can include methods and other fields. The compiler automatically adds some special methods when it creates an enum. For example, they have a static values method that returns an array containing all of the values of the enum in the order they are declared.

Are enums static by default?

Yes, enums are effectively static.

Should enum fields be final?

Enum fields are treated like fields on any other type. Again, because enum values are constants, fields should be final variables, since they will not change.

Is enum mutable?

The types of all fields of the enum are deeply immutable. For example, use ImmutableList and ImmutableSet instead of List and Set . Types are considered immutable if they are primitives, in a set of types that are built in to Error Prone (e.g. java.


2 Answers

Check out Java Language Specification, Third Edition, Section 8.9 at http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.9

It is a compile-time error to reference a static field of an enum type that is not a compile-time constant (§15.28) from constructors, instance initializer blocks, or instance variable initializer expressions of that type. It is a compile-time error for the constructors, instance initializer blocks, or instance variable initializer expressions of an enum constant e to refer to itself or to an enum constant of the same type that is declared to the right of e.

Discussion

Without this rule, apparently reasonable code would fail at run time due to the initialization circularity inherent in enum types. (A circularity exists in any class with a "self-typed" static field.) Here is an example of the sort of code that would fail:

enum Color {
        RED, GREEN, BLUE;
        static final Map<String,Color> colorMap = 
        new HashMap<String,Color>();
        Color() {
            colorMap.put(toString(), this);
        }
    } 

Static initialization of this enum type would throw a NullPointerException because the static variable colorMap is uninitialized when the constructors for the enum constants run. The restriction above ensures that such code won't compile.

like image 66
Jeremiah Orr Avatar answered Nov 15 '22 09:11

Jeremiah Orr


Written out in a roughly equivalent, simpler way, closer to the byte code, we see:

public final class Foo {
    public static final Foo A = new Foo();
    public static final Foo B = new Foo();

    private Foo[] foos;

    private Foo() {
        this.foos = new Foo[] { Foo.A };
    }
}

You can see that to initialise A we are calling the constructor, which reads A. Obviously, whilst still in the constructor A will not have been initialised.

(As it turns out, this simpler code does compile. It just doesn't do what you might expect.)

You probably want Foo.values() instead of the foos instance variable.

like image 33
Tom Hawtin - tackline Avatar answered Nov 15 '22 09:11

Tom Hawtin - tackline