Статическая инициализация перед элементами Enum

Невозможно выполнить статическую инициализацию перед инициализацией элементов перечисления в Java, поскольку элементы перечисления всегда должны быть сначала объявлены и инициализированы.

Если статическая инициализация не зависит от элементов перечисления, порядок ее выполнения после элементов не является проблемой. В противном случае, если инициализация зависит от элементов перечисления, это вызовет проблемы. Однако перечисления Java предоставляют метод values(), способный удовлетворить эту зависимость. Но вызов values() может быть накладным, потому что метод values() каждый раз копирует внутренний статический массив.

Например (отредактировано в сообщении Почему конструктор перечисления не может получить доступ к статическим полям?):

public enum Day {
    SUNDAY("Sun"), MONDAY("Mon"), TUESDAY("Tue"), WEDNESDAY("Wed"), THURSDAY("Thu"), FRIDAY("Fri"), SATURDAY("Sat");

    private final String abbreviation;

    private static final Map<String, Day> ABBREV_MAP = new HashMap<String, Day>();

    static
    {
        for(Day day:values())
            ABBREV_MAP.put(day.abbreviation, day);
    }
    private Day(String abbreviation) {
        this.abbreviation = abbreviation;
        //ABBREV_MAP.put(abbreviation, this); this is not valid
    }

    public String getAbbreviation() {
        return abbreviation;
    }

    public static Day getByAbbreviation(String abbreviation) {
        return ABBREV_MAP.get(abbreviation);
    }
}

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

Мой вопрос заключается в том, что, поскольку обязательство инициализации элементов перечисления сначала может привести к ненужным накладным расходам, можно ли устранить эти накладные расходы или, другими словами, возможно ли выполнить статическую инициализацию перед элементами перечисления? Если нет, то только ради удобочитаемости кода? Я предполагаю, что объявление элементов enum сначала не имеет ничего общего с безопасной инициализацией, как упоминалось в некоторых ответах.

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


person oak    schedule 12.05.2017    source источник
comment
Эти накладные расходы составляют всего несколько микросекунд (вероятно, меньше) и будут выполняться в вашей программе только один раз. Я уверен, что в вашем коде есть лучшие кандидаты для настройки производительности.   -  person assylias    schedule 12.05.2017
comment
Я имею в виду, почему он не идеален, как и ожидалось от отличного языка программирования, несмотря на то, что он такой простой? Или это только для читаемости кода?   -  person oak    schedule 12.05.2017
comment
Ознакомьтесь с ответами на этот вопрос, так как они, похоже, предоставляют более подробную информацию, но не уверены, достаточно ли этого для вашего вопроса, Вот почему я голосую за открытие.   -  person Kelly S. French    schedule 12.05.2017


Ответы (2)


Как ментальная йога (подумайте, что должен делать компилятор):

public enum Day {

    SUNDAY(A.Sun), MONDAY(A.Mon), TUESDAY(A.Tue),
    WEDNESDAY(A.Wed), THURSDAY(A.Thu), FRIDAY(A.Fri), SATURDAY(A.Sat);

    private enum A {
        Sun(Day.SUNDAY), Mon(Day.MONDAY), Tue(Day.TUESDAY), Wed(Day.WEDNESDAY),
        Thu(Day.THURSDAY), Fri(Day.FRIDAY), Sat(Day.SATURDAY);

        private final Day day;

        A(Day day) {
            this.day = day;
        }
    }

    private final A abbrev;

    Day(A abbrev) {
        this.abbrev = abbrev;
    }

    public static Day getByAbbreviation(String abbreviation) {
        return A.valueOf(abbreviation).day;
    }
}

Слишком извращенно.

person Joop Eggen    schedule 12.05.2017
comment
Если встроенный метод valueOf использует HashMap внутри, это отменит накладные расходы. Но нет, это может стоить больше, чем HashMap при доступе к элементам. Однако вы подсказали мне, что мы можем использовать обычные внутренние классы таким образом и реализовать внутри них HashMap. Я думал, что невозможно устранить эти накладные расходы. Внутренние статические классы становятся обходными путями для статической инициализации. Спасибо за ваш мозговой штурм! - person oak; 22.05.2017
comment
Я внес изменения в код. Пожалуйста, проверьте мой ответ! - person oak; 22.05.2017
comment
Фактически, сокращение перечисления до трех букв уже сделало бы это. ;) - person Joop Eggen; 22.05.2017
comment
Но в жизни не всегда так :) - person oak; 22.05.2017

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

Мой окончательный код будет следующим для таблицы значений и конструктора, статический блок будет удален, а накладные расходы будут устранены:

//HashMap in inner class
public static class InnerMap
{
    private static final Map<String, Day> ABBREV_MAP = new HashMap<String, Day>();
}
//Constructor
private Day(String abbreviation) {
    this.abbreviation = abbreviation;
    InnerMap.ABBREV_MAP.put(abbreviation, this);
}

Это работает, потому что статическая инициализация происходит при первом использовании класса, в данном случае внутреннего класса.

person oak    schedule 22.05.2017