Java Enum Static Final Instance Variables

33,070

Solution 1

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);
}

Solution 2

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.

Solution 3

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());
    }
Share:
33,070
skeggse
Author by

skeggse

Updated on June 22, 2020

Comments

  • skeggse
    skeggse almost 4 years

    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;
      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?

  • ColinD
    ColinD almost 13 years
    The bug you linked refers to accessing the .class literal of the enum class, not to referencing an actual static field of the enum.