Порядок выполнения статических блоков в типе Enum по отношению к конструктору

Это из эффективной Java:

// Implementing a fromString method on an enum type
  private static final Map<String, Operation> stringToEnum
      = new HashMap<String, Operation>();

  static { // Initialize map from constant name to enum constant
    for (Operation op : values())
      stringToEnum.put(op.toString(), op);
  }

  // Returns Operation for string, or null if string is invalid
  public static Operation fromString(String symbol) {
    return stringToEnum.get(symbol);
  }

Обратите внимание, что константы операции помещаются в карту stringToEnum из статического блока, который запускается после создания констант. Попытка поместить каждую константу в карту из собственного конструктора вызовет ошибку компиляции. Это хорошо, потому что если бы это было допустимо, это вызвало бы NullPointerException. Конструкторам перечисления не разрешен доступ к статическим полям перечисления, за исключением полей констант времени компиляции. Это ограничение необходимо, поскольку эти статические поля еще не были инициализированы при запуске конструкторов.

Мой вопрос касается строки:

«Обратите внимание, что константы операции помещаются в карту stringToEnum из статического блока, который запускается после создания констант».

Я думал, что статический блок выполняется до запуска конструктора. Они фактически выполняются во время загрузки класса.

Что мне здесь не хватает?


person Geek    schedule 02.08.2012    source источник
comment
См. stackoverflow.com/questions/3028219/   -  person John Watts    schedule 02.08.2012


Ответы (3)


Я понимаю ваш вопрос так: почему существует гарантия того, что константы перечисления будут инициализированы до запуска статического блока. Ответ дан в JLS, а конкретный пример приведен в #8.9.2.1, где следующее объяснение:

статическая инициализация происходит сверху вниз.

а константы перечисления неявно являются конечными статическими и объявляются перед статическим блоком инициализатора.

ИЗМЕНИТЬ

Поведение не отличается от обычного класса. Код ниже печатает:

In constructor: PLUS
PLUS == null MINUS == null

In constructor: MINUS
PLUS != null MINUS == null

In static initialiser
PLUS != null MINUS != null

In constructor: after static
PLUS != null MINUS != null
public class Operation {

    private final static Operation PLUS = new Operation("PLUS");
    private final static Operation MINUS = new Operation("MINUS");

    static {
        System.out.println("In static initialiser");
        System.out.print("PLUS = " + PLUS);
        System.out.println("\tMINUS = " + MINUS);
    }

    public Operation(String s) {
        System.out.println("In constructor: " + s);
        System.out.print("PLUS = " + PLUS);
        System.out.println("\tMINUS = " + MINUS);
    }

    public static void main(String[] args) {
        Operation afterStatic = new Operation ("after static");
    }    
}
person assylias    schedule 02.08.2012
comment
скажем, операция представляет собой операцию калькулятора. И константы (PLUS, MINUS, DIVIDE, MULTIPLY) будут созданы только после запуска конструктора. Поэтому я смущен тем, как константы могут быть инициализированы до запуска конструктора. Неявные статические окончательные поля, содержащие константы, могут быть созданы до конструктора, но они могут быть назначены только после запуска конструктора. - person Geek; 02.08.2012
comment
каждая константа (PLUS, MINUS...) будет инициализирована после запуска соответствующего конструктора. Порядок выполнения, если хотите, будет такой: PLUS = new Operation(...); MINUS = new Operation(...); ... ; static block - person assylias; 02.08.2012
comment
Вот почему Блох говорит, что если вы попытаетесь заполнить карту из конструкторов, потому что константы не были инициализированы до тех пор, пока все конструкторы не закончат работу. - person assylias; 02.08.2012
comment
поэтому конструкторы фактически запускаются перед статическим блоком. Это означает, что поведение Enum отличается от других обычных классов. Верно ? - person Geek; 02.08.2012
comment
Нет, это не так - я добавил в свой ответ пример с обычным классом. Перечисление выполняется аналогичным образом, за исключением того, что часть его неявна. - person assylias; 02.08.2012
comment
В примере показано, что PLUS и MINUS могут быть нулевыми в конструкторе, но инициализируются их правильным значением после выполнения статического блока. - person assylias; 02.08.2012
comment
это означает принятый ответ на этот вопрос (stackoverflow.com/ вопросов/3987428/) неверно. Смотрите порядок печати. Первая строка печатает статическую инициализацию. - person Geek; 02.08.2012
comment
давайте продолжим это обсуждение в чате - person Geek; 02.08.2012

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

person Adam Monos    schedule 02.08.2012

Operation константы — это статические поля, созданные в статическом блоке в порядке появления.

static { 
    // instantiate enum instances here
    ...
    // Initialize map from constant name to enum constant     
    for (Operation op : values())       
       stringToEnum.put(op.toString(), op);   
} 
person Prince John Wesley    schedule 02.08.2012