Статические конечные переменные экземпляра Java Enum

ура!

Этот код работал некоторое время, затем я решил добавить цвет по умолчанию, и он перестает работать. Я получаю следующую ошибку:

1 error found:
File: Status.java  [line: 20]
Error: Status.java:20: illegal reference to static field from initializer

Со следующим кодом во время компиляции.

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

Это должно работать, насколько я могу судить, но по какой-то причине Java решила выбросить ошибку. Любые мысли?

Ответ 1

defaultColor будет инициализироваться только после того, как конструкторы были вызваны, поэтому до этого времени оно будет иметь значение по умолчанию (null). Один из вариантов - поместить цвет по умолчанию в вложенный тип:

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

Конечно, если вы только ссылаетесь на цвет по умолчанию один раз в коде, вы можете также жестко закодировать его в вызове конструктора:

Status(String name)
{
  this(name, Color.WHITE);
}

Ответ 2

Сначала необходимо инициализировать константы перечисления. Чтобы инициализировать их, необходимо вызвать конструкторы. Первый конструктор ссылается на статическое поле, которое, возможно, не было инициализировано в момент его вызова.

Ответ 3

Java позволяет это

class Status
{
    public static final Status OFF = new Status("Off");

    public static final Color defaultColor = Color.WHITE;

    Status(String name)
    {
      this(name, defaultColor);
    }
}

Конечно, это будет проблемой во время выполнения, но Java все равно. Задача программиста - упорядочить последовательности init, и компилятор не просто проверить все разбитые init-зависимости. В любом случае проблему легко исправить:

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

Но для enum это обходное решение не применяется, потому что статические поля в типе enum не могут быть перемещены до самих перечислений (возможно, по чистой синтаксической причине). Чтобы избежать путаницы, Java добавляет дополнительное ограничение для enum - статические поля не могут ссылаться на конструктор.

Это ограничение наполовину. Нелегко (если не невозможно) проверить все возможные применения статических полей из конструктора. Следующий код будет компилироваться, преодолев ограничение:

enum Status
{
    OFF("Off");

    public static final Color defaultColor = Color.WHITE;
    static Color defaultColor(){ return defaultColor; }

    Status(String name)
    {
      this(name, defaultColor());
    }