Java Enum Static Final Instance Variables
Huzzah!
This code worked for a time, then I decided to add a default color, and it stopped working. I get the following error:
1 error found:
File: Status.java [line: 20]
Error: Status.java:20: illegal reference to static field from initializer
With the following code at compile-time.
import java.awt.Color;
enum Status
{
OFF ("Off"),
TRAINING ("Training", new Color(255, 191, 128)),
BEGINNER ("Beginner", new Color(128, 255, 138)),
INTERMEDIATE ("Intermediate", new Color(128, 212, 255)),
ADVANCED ("Advanced", new Color(255, 128, 128));
public final String name;
开发者_Python百科 public final Color color;
public static final Color defaultColor = Color.WHITE;
Status(String name)
{
this(name, defaultColor);
}
Status(String name, Color color)
{
this.name = name;
this.color = color;
}
}
This should work, as far as I can tell, but for whatever reason Java decided to throw an error. Any thoughts?
defaultColor
will only be initialized after the constructors have been called - so it will have its default value (null) until that time. One option would be to put the default colour in a nested type:
import java.awt.Color;
enum Status
{
OFF ("Off"),
TRAINING ("Training", new Color(255, 191, 128)),
BEGINNER ("Beginner", new Color(128, 255, 138)),
INTERMEDIATE ("Intermediate", new Color(128, 212, 255)),
ADVANCED ("Advanced", new Color(255, 128, 128));
public final String name;
public final Color color;
Status(String name)
{
this(name, Defaults.COLOR);
}
Status(String name, Color color)
{
this.name = name;
this.color = color;
}
private static class Defaults
{
private static Color COLOR = Color.WHITE;
}
}
Of course, if you're only referring to the default colour once in the code, you might as well hard-code it within the constructor call:
Status(String name)
{
this(name, Color.WHITE);
}
The enum constants have to be initialized first. To initialize them, the constructors must be called. The first constructor references a static field that could not possibly have been initialized at the time it is called.
Java allows this
class Status
{
public static final Status OFF = new Status("Off");
public static final Color defaultColor = Color.WHITE;
Status(String name)
{
this(name, defaultColor);
}
}
Of course it'll have problem at runtime, but Java doesn't care. It's the job of the programmer to arrange init sequences, and it is not easy for compiler to check all broken init dependencies. The problem is easy to fix anyway:
class Status
{
// now it works, this field is initialized first
public static final Color defaultColor = Color.WHITE;
public static final Status OFF = new Status("Off");
But for enum
, this workaround does not apply, because the static fields in an enum
type cannot be moved before enums themselves (probably for pure syntactic reason). To avoid confusion, Java adds an additional restriction for enum
- static fields cannot be referenced from constructor.
This restriction is half-assed. It's not easy (if not impossible) to check all possible usages of static fields from constructor. The following code would compile, defeating the restriction:
enum Status
{
OFF("Off");
public static final Color defaultColor = Color.WHITE;
static Color defaultColor(){ return defaultColor; }
Status(String name)
{
this(name, defaultColor());
}
Discussion and Possible workaround
精彩评论