Могу ли я добавлять и удалять элементы перечисления во время выполнения в Java
Можно ли добавлять и удалять элементы из перечисления в Java во время выполнения?
Например, могу ли я прочитать в метках и аргументах конструктора перечисления из файла?
@saua, вопрос только в том, действительно ли это можно сделать из интереса. Я надеялся, что будет какой-то изящный способ изменить работающий байт-код, возможно, используя BCEL или что-то в этом роде. Я также задал этот вопрос, потому что понял, что не совсем уверен, когда следует использовать enum.
Я вполне уверен, что правильным ответом будет использование коллекции, которая обеспечивает уникальность, а не enum, если я хочу иметь возможность безопасно изменять содержимое во время выполнения.
6 ответов
Нет, перечисления должны быть полным статическим перечислением.
Во время компиляции вы можете захотеть сгенерировать свой файл enum .java из другого исходного файла. Вы даже можете создать файл.class, как этот.
В некоторых случаях вам может потребоваться набор стандартных значений, но разрешить расширение. Обычный способ сделать это, есть interface
для интерфейса и enum
что реализует это interface
для стандартных значений. Конечно, вы теряете способность switch
когда у вас есть только ссылка на interface
,
За занавесом перечисления - это POJO с закрытым конструктором и набором открытых статических конечных значений типа перечисления (см. Пример здесь). Фактически, до Java5 считалось, что наилучшей практикой является создание собственного перечисления таким образом, и Java5 представила enum
Ключевое слово как сокращение. См. Источник для Enum
Таким образом, не должно быть никаких проблем написать свой собственный TypeSafeEnum с общедоступным статическим окончательным массивом констант, которые читаются конструктором или передаются ему.
Кроме того, сделайте себе одолжение и переопределите equals
, hashCode
а также toString
и, если возможно, создать values
метод
Вопрос в том, как использовать такое динамическое перечисление... вы не можете прочитать значение "PI=3.14" из файла для создания enum MathConstants
а затем идти вперед и использовать MathConstants.PI
где угодно...
Мне нужно было сделать что-то подобное (для целей модульного тестирования), и я столкнулся с этим - EnumBuster: http://www.javaspecialists.eu/archive/Issue161.html
Это позволяет добавлять, удалять и восстанавливать значения перечислений.
Изменить: я только начал использовать это, и обнаружил, что есть некоторые небольшие изменения, необходимые для Java 1.5, с которой я в настоящее время застрял:
- Добавить массив copyOf статических вспомогательных методов (например, взять эти версии 1.6: http://www.docjar.com/html/api/java/util/Arrays.java.html)
- Измените EnumBuster.undoStack на стек
<Memento>
- В undo() измените undoStack.poll() на undoStack.isEmpty()? null: undoStack.pop();
- Строка VALUES_FIELD должна быть "ENUM$VALUES" для перечислений java 1.5, которые я пробовал до сих пор.
Я столкнулся с этой проблемой на формирующем проекте моей молодой карьеры.
Подход, который я выбрал, заключался в том, чтобы сохранять значения и имена перечисления извне, и конечная цель состояла в том, чтобы иметь возможность писать код, который выглядел как можно ближе к перечислению языка.
Я хотел, чтобы мое решение выглядело так:
enum HatType
{
BASEBALL,
BRIMLESS,
INDIANA_JONES
}
HatType mine = HatType.BASEBALL;
// prints "BASEBALL"
System.out.println(mine.toString());
// prints true
System.out.println(mine.equals(HatType.BASEBALL));
И у меня получилось что-то вроде этого:
// in a file somewhere:
// 1 --> BASEBALL
// 2 --> BRIMLESS
// 3 --> INDIANA_JONES
HatDynamicEnum hats = HatEnumRepository.retrieve();
HatEnumValue mine = hats.valueOf("BASEBALL");
// prints "BASEBALL"
System.out.println(mine.toString());
// prints true
System.out.println(mine.equals(hats.valueOf("BASEBALL"));
Поскольку мои требования заключались в том, что должна быть возможность добавлять члены в enum во время выполнения, я также реализовал эту функциональность:
hats.addEnum("BATTING_PRACTICE");
HatEnumRepository.storeEnum(hats);
hats = HatEnumRepository.retrieve();
HatEnumValue justArrived = hats.valueOf("BATTING_PRACTICE");
// file now reads:
// 1 --> BASEBALL
// 2 --> BRIMLESS
// 3 --> INDIANA_JONES
// 4 --> BATTING_PRACTICE
Я назвал его "шаблоном" динамического перечисления, и вы читаете об оригинальном дизайне и его пересмотренном издании.
Разница между ними заключается в том, что пересмотренное издание было разработано после того, как я действительно начал заниматься OO и DDD. Первый, который я разработал, когда я все еще писал номинально процедурный DDD, испытывал не меньше времени.
Вы можете загрузить класс Java из исходного кода во время выполнения. (Используя JCI, BeanShell или JavaCompiler)
Это позволит вам изменять значения Enum по вашему желанию.
Примечание: это не изменит классы, которые ссылаются на эти перечисления, так что это может быть не очень полезно в реальности.
Рабочий пример широко используется в модифицированном Minecraft. См. Методы EnumHelper.addEnum() на Github
Однако обратите внимание, что в редких случаях практический опыт показал, что добавление членов Enum может привести к некоторым проблемам с оптимизатором JVM. Точные проблемы могут отличаться в зависимости от JVM. Но в целом кажется, что оптимизатор может предположить, что некоторые внутренние поля Enum, в частности размер Enum.values()
массив, не изменится. См. Обсуждение проблемы. Рекомендуемое решение - не делать.values()
горячая точка для оптимизатора. Поэтому, если добавление к членам Enum во время выполнения, это должно выполняться один раз и только один раз, только когда приложение инициализировано, а затем результат.values()
следует кэшировать, чтобы не превращать его в точку доступа.
То, как работает оптимизатор и как он обнаруживает горячие точки, неясно и может различаться между разными JVM и разными сборками JVM. Если вы не хотите рисковать этим типом проблем в производственном коде, не меняйте Enums во время выполнения.
Вы можете попытаться присвоить свойства ENUM, который вы пытаетесь создать, и статически построить его, используя загруженный файл свойств. Большой взлом, но это работает:)