Когда определены значения enum?

Я пытаюсь реализовать что-то полуэффективно. В моей программе есть перечислитель, представляющий метрики, которые используются для передачи информации в программе.

У меня есть класс, который позволяет мне "объединять" эти метрики в один объект, чтобы я мог по существу создать "предложение", чтобы дать мне сложную информацию без необходимости какого-либо сложного анализа информации. По сути, это битовые флаги, но поскольку я использую enum, их может быть больше, чем 64.

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

metrics = new boolean[Metrics.values().length];

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

private static final int METRIC_COUNT = Metrics.values().length;

Я могу это сделать? Или компилятор не определил перечисленные значения до того, как объявил эту статическую переменную? Я знаю, что это не лучшее объяснение моего вопроса, но я не уверен, как еще это сформулировать.

Я знаю, что статическая переменная будет определена во время выполнения (если назначенное ей значение не было буквальным значением), поэтому сможет ли программа запрашивать членов перечисления Metrics в этот момент выполнения?

Ответ 1

EnumSet

"объединить" эти метрики в один объект

по сути битовые флаги

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

Если это так, нет необходимости кататься самостоятельно. Используйте EnumSet или EnumMap. Это специальные реализации интерфейсов Set и Map. Эти классы чрезвычайно эффективны из-за того, что они обрабатывают перечисления природы, занимают очень мало памяти и очень быстро выполняются.

Взять, к примеру, встроенное перечисление DayOfWeek. Определяет семь объектов, по одному на каждый день недели в календаре ISO 8601.

Set< DayOfWeek > weekend = EnumSet.of( DayOfWeek.SATURDAY , DayOfWeek.SUNDAY ) ;

Используйте удобные методы Set такие как contains.

boolean isTodayWeekend = weekend.contains( LocalDate.now().getDayOfWeek() ) ;

Если вы зацикливаете элементы набора, их обещают предоставить в том порядке, в котором они определены в перечислении (их "естественный" порядок). Таким образом, по логике EnumSet, EnumSet должен был быть помечен как SortedSet, но загадочным образом не был так помечен. Тем не менее, вы знаете порядок. Например, цикл EnumSet.allOf( DayOfWeek.class ) отображает DayOfWeek.MONDAY сначала, а DayOfWeek.SUNDAY последний (согласно стандарту ISO 8601).

Когда определены значения Enum?

Элементы enum определяются во время компиляции и не могут быть изменены во время выполнения (если вы не используете трюки с отражением). Каждая именованная переменная заполняется экземпляром при загрузке класса. См. Раздел 8.9, Типы перечислений спецификации языка Java.

Если вы определите enum Pet с помощью DOG, CAT и BIRD, то вы будете знать, что у вас будет ровно три экземпляра во время выполнения.

Вы можете посчитать количество элементов, определенных в перечислении, как минимум двумя способами:

  • Вызов метода values сгенерированного компилятором для любого enum, где вы можете задать размер результирующего массива, как показано в вашем Вопросе.
    int countDows = DayOfWeek.values().length() ;
  • Вызов Set::size после создания EnumSet всех экземпляров перечисления.
    int countDows = EnumSet.allOf( DayOfWeek.class).size() ;

Ответ 2

private static final int METRIC_COUNT = Metrics.values().length;

Я могу это сделать?

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

Я знаю, что статическая переменная будет определена во время выполнения (если назначенное ей значение не было буквальным значением), поэтому сможет ли программа запрашивать членов перечисления Metrics в этот момент выполнения?

Ага. Как определено, METRIC_COUNT также вычисляется во время выполнения.

Ответ 3

Я не знаю, нужно ли вам делать это в этом случае, но я думаю, что понимаю более общую проблему неспособности вычислить константу в начале выполнения. У меня была эта проблема. Решение, которое я использовал, выглядит примерно так: вычислите METRICS_COUNT позже, когда вы будете готовы его использовать и уверены, что объект Metrics определен, но все же только один раз:

private static int METRIC_COUNT = -1;

public static int getMetricsCount() {
    if (METRIC_COUNT < 0)
        METRIC_COUNT = Metrics.values().length;
    return METRIC_COUNT;
}

Обратите внимание, что я не рассматриваю проблему Enum конкретно, как и другие в комментариях. Я просто предлагаю способ отложить установку глобального значения до того момента, когда это необходимо.