Можно ли наследовать перечисления для добавления новых элементов?
Я хочу взять уже существующий enum и добавить в него новые элементы следующим образом:
enum A {a, b, c}
enum B extends A {d}
/*B = {a, b, c, d}*/
Возможно ли это сделать в Java?
5 ответ(ов)
Нет, вы не можете сделать это в Java. Кроме всего прочего, d
тогда, по всей видимости, будет экземпляром класса A
(с учетом обычного понимания "наследования"), но пользователи, знающие только о A
, не будут об этом знать, что сводит на нет саму суть enum как хорошо известного множества значений.
Если вы расскажете подробнее о том, как вы планируете использовать это, мы могли бы предложить альтернативные решения.
Enums представляют собой полное перечисление возможных значений. Поэтому (возможно, не столь полезный) ответ - нет.
В качестве реальной задачи можно рассмотреть дни недели: будние дни, выходные и, соответственно, дни недели. Мы могли бы определить все дни в рамках дней недели, но тогда не сможем представить свойства, уникальные для будних и выходных дней.
Что мы можем сделать, так это создать три типа enum с отображением между будними/выходными днями и днями недели.
public enum Weekday {
MON, TUE, WED, THU, FRI;
public DayOfWeek toDayOfWeek() { ... }
}
public enum WeekendDay {
SAT, SUN;
public DayOfWeek toDayOfWeek() { ... }
}
public enum DayOfWeek {
MON, TUE, WED, THU, FRI, SAT, SUN;
}
Альтернативный подход - создать расширяемый интерфейс для дней недели:
interface Day {
...
}
public enum Weekday implements Day {
MON, TUE, WED, THU, FRI;
}
public enum WeekendDay implements Day {
SAT, SUN;
}
Или мы могли бы совместить оба подхода:
interface Day {
...
}
public enum Weekday implements Day {
MON, TUE, WED, THU, FRI;
public DayOfWeek toDayOfWeek() { ... }
}
public enum WeekendDay implements Day {
SAT, SUN;
public DayOfWeek toDayOfWeek() { ... }
}
public enum DayOfWeek {
MON, TUE, WED, THU, FRI, SAT, SUN;
public Day toDay() { ... }
}
Таким образом, благодаря этим подходам мы можем эффективно моделировать дни недели, учитывая уникальные свойства будних и выходных дней, избегая при этом ограничений, связанных с использованием одного только enum.
Внутри реализации ваше ENUM - это просто обычный класс, сгенерированный компилятором. Этот сгенерированный класс наследует java.lang.Enum
. Техническая причина, по которой вы не можете расширять сгенерированный класс, заключается в том, что он объявлен как final
. Концептуальные причины его финальности обсуждаются в этой теме. Однако я добавлю некоторые механизмы в обсуждение.
Вот тестовый enum:
public enum TEST {
ONE, TWO, THREE;
}
Сгенерированный код от javap:
public final class TEST extends java.lang.Enum<TEST> {
public static final TEST ONE;
public static final TEST TWO;
public static final TEST THREE;
static {};
public static TEST[] values();
public static TEST valueOf(java.lang.String);
}
Теоретически, вы могли бы самостоятельно реализовать этот класс и убрать final
. Но компилятор не позволяет вам напрямую расширять java.lang.Enum
. Вы также можете решить не наследовать от java.lang.Enum
, но тогда ваш класс и его производные классы не будут являться экземплярами java.lang.Enum
, что, возможно, для вас не будет иметь значения!
Привет! Ваш вопрос касается использования перечислений (enum) в Java и их группировки для обработки событий, что действительно может облегчить работу с разными состояниями и уменьшить количество кода в условных операторах.
Вы привели очень интересный пример с перечислениями для статусов операций банкомата. Ваше решение позволяет связывать события с их группами, что делает код более читаемым и структурированным. Давайте рассмотрим более подробно, как это работает и как это может быть полезно.
Ваша основная идея заключается в том, чтобы создать несколько групп состояний (в данном случае, State_StatusGroup
), каждая из которых содержит члены, представляющие операции статуса. Именно использование интерфейса StatusGroupInterface
позволяет достичь некоторой степени многократного наследования для перечислений.
Преимущества такого подхода:
Явные метаданные: Вместо того чтобы просто полагаться на строковые значения или отдельные перечисления, вы добавляете метаинформацию прямо в сами перечисления. Это делает код самодокументированным, и его легче понимать без необходимости погружаться в детали.
Многократное наследование: Ваши перечисления могут группироваться по различным аспектам. Например, события банкомата можно классифицировать по статусу, типу операции и другим критериям.
Компактный код: Вместо множества условных операторов
switch
, вы можете использовать методis
для проверки статуса события. Это не только уменьшает количество кода, но и делает его более понятным.
Пример использования
Ваш пример с арганизацией обработки событий выглядит очень наглядно. Вы определяете, что делать при определенных событиях, таких как START
, STEP
, и FINISH
, при помощи простых условий:
if(myEvent.is(State_StatusGroup.START)) {
makeNewOperationObject();
} else if(myEnum.is(State_StatusGroup.STEP)) {
makeSomeSeriousChanges();
} else if(myEnum.is(State_StatusGroup.FINISH)) {
closeTransactionOrSomething();
}
Надстройки
Вы также упомянули о более сложном примере с AtmEventType
, где перечисления связываются с несколькими группами, что позволяет продвинутым пользователям обращаться с различными аспектами события, такими как статус, авторизация и изменения состояния счета. Это действительно мощный способ структурирования вашего кода.
public enum AtmEventType {
USER_DEPOSIT(Status_EventsGroup.WITH_STATUS, ...),
// другие события
}
Заключение
Ваш подход предоставляет отличный способ организации логики обработки событий с использованием перечислений в Java. Это не только упрощает код, делая его более удобочитаемым, но также вводит дополнительные структурные элементы, которые могут быть полезны для сложных бизнес-логик. Продолжайте экспериментировать с такими концепциями, и вы увидите, как они могут значительно упростить разработку!
В данном ответе я хотел бы показать, как можно расширить перечисление (enum) с помощью другого перечисления, используя достаточно простой подход.
Предположим, у вас есть перечисление с общими константами:
public interface ICommonInterface {
String getName();
}
public enum CommonEnum implements ICommonInterface {
P_EDITABLE("editable"),
P_ACTIVE("active"),
P_ID("id");
private final String name;
CommonEnum(String name) {
this.name = name;
}
@Override
public String getName() {
return this.name;
}
}
Теперь вы можете попробовать создать новое перечисление, которое будет "расширять" предыдущее, вот так:
public enum SubEnum implements ICommonInterface {
P_EDITABLE(CommonEnum.P_EDITABLE),
P_ACTIVE(CommonEnum.P_ACTIVE),
P_ID(CommonEnum.P_ID),
P_NEW_CONSTANT("new_constant");
private final String name;
SubEnum(CommonEnum commonEnum) {
this.name = commonEnum.getName();
}
SubEnum(String name) {
this.name = name;
}
@Override
public String getName() {
return this.name;
}
}
Однако, есть важный нюанс: всякий раз, когда вам нужно будет добавить новую константу в SubEnum
, вам придется вручную изменять файлы SubEnum
. Это стоит учитывать при проектировании архитектуры вашего кода, поскольку может увеличивать объем работы при добавлении новых значений.
Сравнение членов enum в Java: использовать == или equals()?
Цикл for для перебора enum в Java
Хорошая ли практика использовать порядковый номер enum?
Jackson: Сериализация и десериализация значений enum в виде целых чисел
Являются ли имена перечислений (enum) в Java интернированными?