I wanted to create an enum
where each constant has a Map
associated with it. I accomplished this by giving each constant an instance initializer, like so:
import java.util.HashMap;
import java.util.Map;
public enum Derp {
FOO {{
mMap.put("bar", 1);
}};
// cannot be private
protected final Map<String, Integer> mMap = new HashMap<>();
}
I found that if mMap
is private
, it cannot be referenced in the instance initializer. The error is Cannot make a static reference to the non-static field mMap
. Before the reason for this occurred to me, I consulted JLS §8.9.2, which says in part:
It is a compile-time error for the constructors, instance initializer blocks, or instance variable initializer expressions of an enum constant
e
to refer toe
or to an enum constant of the same type that is declared to the right ofe
.
Aren't I breaking this rule by implicitly referencing FOO
in FOO
's own instance intializer? How does this compile? It not only compiles, but works correctly at runtime.
(It occurred to me that mMap
cannot be private
because I'm implicitly creating an anonymous subclass which cannot reference a private
field in its superclass. Which is a bit weird in itself since enums are implicitly final
...)
Java enums are syntactic sugar. Java compiler compiles an enum class to a simple Java class extending java.
Enum values or Enum instances are public, static, and final in Java. They are a compile-time constant, which means you can not changes values of Enum instances and further assignment will result in a compile-time error.
The use of enum here is guaranteed to occupy no storage in the object, and the enumerators are all evaluated at compile time. You can also explicitly establish the values of the enumerators: enum { one = 1, two = 2, three };
An enumeration, or Enum , is a symbolic name for a set of values. Enumerations are treated as data types, and you can use them to create sets of constants for use with variables and properties.
It is a compile-time error for the constructors, instance initializer blocks, or instance variable initializer expressions of an enum constant
e
to refer toe
or to an enum constant of the same type that is declared to the right ofe
.
The specification here just means you can't reference by name because the field referred to by e
is not initialized yet. It doesn't mean you can't access this
.
It's basically the same rule as any other initializer (like int x = x;
).
We can see why with an example like (Ideone):
enum Example {
INSTANCE {{
subversion();
}};
static void subversion() {
System.out.println(INSTANCE);
}
public static void main(String[] args) {
System.out.println(INSTANCE);
}
}
Which outputs
null
INSTANCE
I found that if
mMap
is private, it cannot be referenced in the instance initializer.
You can qualify the call as super.mMap.put(...);
. The private mMap
is not inherited, but it's accessible from the inner class. I also covered this here. The short of it is that the simple name mMap
refers to a non-existent outer instance of Derp
.
We can verify this is the case with an example like (Ideone):
class Example {
private int x;
class Inner extends Example {{
x = 1; // refers to the outer instance
super.x = 2; // refers to the inner instance
}}
public static void main(String[] args) {
Example outer = new Example();
Example inner = outer.new Inner();
System.out.println(outer.x); // prints 1
System.out.println(inner.x); // prints 2
}
}
Except in your case FOO
is static so there is no outer instance—hence compiler error.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With