Почему инициализация констант перечисления Java не завершена?

я наткнулся на очень странный баг и я не могу объяснить, почему это происходит. Представьте себе следующее перечисление:

import java.awt.Color;

public class test {

    /**
     * @param args
     */
    public static void main(String[] args) {
        System.out.println(MyEnum.CONSTANT1.get());
        System.out.println(MyEnum.CONSTANT2.get());
    }

    private enum MyEnum {
        CONSTANT1(staticMethod1()),
        CONSTANT2(staticMethod2());

        private static final Color WHY_AM_I_NULL = new Color(255, 255, 255);

        private final Color color;

        private MyEnum(Color color) {
            this.color = color;
        }

        public Color get() {
            return color;
        }

        private static Color staticMethod1() {
            return new Color(100, 100, 100);
        }

        private static Color staticMethod2() {
            return WHY_AM_I_NULL;
        }
    }

}

результаты при запуске этого:

java.awt.Color[r=100,g=100,b=100]
null

вопрос в том, почему второй равен null?

поправка: Если вы помещаете WHY_AM_I_NULL в частный статический класс внутри перечисления, то он инициализируется первым.

4 ответов


проблема в том, что все статические поля (и экземпляры перечисления считаются таковыми) инициализируются в их объявленном порядке (спецификация). Так когда CONSTANT2 создается экземпляр, поле WHY_AM_I_NULL по-прежнему не инициализируется (и, следовательно,null).

поскольку вы не можете поместить поле перед экземплярами перечисления, вам нужно найти другой способ сделать то, что вы хотите (например, поместить поле вне класса перечисления). Если вы скажете нам, чего вы действительно хотите достичь, можно сделать дополнительные предложения.

Edit: если поставить WHY_AM_I_NULL во вложенном классе поля этого класса будут инициализированы, как только класс будет впервые доступен (т. е. в этом случае во время выполнения staticMethod2).


перечисления являются функцией компилятора. Фактически компилятор создает класс с именем MyEnum, который содержит 2 открытых статических поля CONSTANT1 и CONSTANT2 и другой код.

статическая инициализация выполняется вверх-вниз, поэтому КОНСТАНТА2 создается и инициализируется перед статической переменной WHY_AM_I_NULL. Вот почему WHY_AM_I_NULL имеет значение null при инициализации CONSTANT2.


WHY_AM_I_NULL равно null, когда staticMethod2 вызывается-вот как JLS указывает инициализации

в другой последовательности вы получите 100, 255 вместо 100, null:

private static final Color WHY_AM_I_NULL = new Color(255, 255, 255);
private enum MyEnum {
    CONSTANT1(staticMethod1()),
    CONSTANT2(staticMethod2());
    //...

это происходит потому, что статические поля (включая значения перечисления) инициализируются в том порядке, в котором они появляются в файле.

так CONSTANT1 и CONSTANT2 быть инициализирована перед WHY_AM_I_NULL, поэтому CONSTANT2 инициализируется с null.