Это основные аргументы для enum
, EnumMap
и EnumSet
на коротких примерах.
case для enum
Начиная с Java 6, java.util.Calendar
является примером грязного класса, который мог бы получить большую пользу от использования enum
(среди других улучшений).
В настоящее время Calendar
определяет следующие константы ( среди многих других ):
// int constant antipattern from java.util.Calendar
public static final int JANUARY = 0;
public static final int FEBRUARY = 1;
...
public static final int SUNDAY = 1;
public static final int MONDAY = 2;
...
Это все int
, хотя они, очевидно, представляют различные концептуальные объекты.
Ниже приведены некоторые серьезные последствия:
- Хрупкие ;необходимо позаботиться о назначении разных номеров при необходимости.
- Если по ошибке мы установим
MONDAY = 0;
, SUNDAY = 0;
, тогда у нас будет MONDAY == SUNDAY
- Нет пространства имен и нет безопасности типов , так как все это просто
int
: - Мы можем
setMonth(JANUARY)
, но мы также можем setMonth(THURSDAY)
или setMonth(42)
- Кто знает, что
set(int,int,int,int,int,int)
(реальный метод!) Делает!
В отличие от этого мы могли бы иметь что-то вроде этого:
// Hypothetical enums for a Calendar library
enum Month {
JANUARY, FEBRUARY, ...
}
enum DayOfWeek {
SUNDAY, MONDAY, ...
}
Теперь у нас никогда нетбеспокоиться о MONDAY == SUNDAY
(это никогда не может произойти!), и поскольку Month
и DayOfWeek
- это разные типы, setMonth(MONDAY)
не компилируется.
Кроме того, здесь есть некоторые до и послекоды:
// BEFORE with int constants
for (int month = JANUARY; month <= DECEMBER; month++) {
...
}
Здесь мы делаем всевозможные предположения, например, JANUARY + 1 == FEBRUARY
и т. д. С другой стороны, копия enum
является одновременно более краткой, более читаемой и делает меньше предположений(и, следовательно, меньше шансов для ошибок):
// AFTER with enum
for (Month month : Month.values()) {
...
}
Случай для полей экземпляра
В Java enum
- это class
, который имеет много специальных свойств, ноclass
тем не менее, позволяя вам определять методы и поля экземпляра при необходимости.
Рассмотрим следующий пример:
// BEFORE: with int constants
public static final int NORTH = 0;
public static final int EAST = 1;
public static final int SOUTH = 2;
public static final int WEST = 3;
public static int degreeFor(int direction) {
return direction * 90; // quite an assumption!
// must be kept in-sync with the int constants!
}
//...
for (int dir = NORTH; dir <= WEST; dir++) {
... degreeFor(dir) ...
}
С другой стороны, с помощью enum
вы можете написать что-то вроде этого:
enum Direction {
NORTH(0), EAST(90), SOUTH(180), WEST(270);
// so obvious! so easy to read! so easy to write! so easy to maintain!
private final int degree;
Direction(int degree) { this.degree = degree; }
public int getDegree() { return degree; }
}
//...
for (Direction dir : Direction.values()) {
... dir.getDegree() ...
}
Случай для методов экземпляра
Рассмотрим следующий пример:
static int apply(int op1, int op2, int operator) {
switch (operator) {
case PLUS : return op1 + op2;
case MINUS : return op1 - op2;
case ...
default: throw new IllegalArgumentException("Unknown operator!");
}
}
Как показано в предыдущем примере, enum
в Java может иметь методы экземпляра, но нетолько это, но каждая константа может также иметь свою собственную @Override
.Это показано в следующем коде:
enum Operator {
PLUS { int apply(int op1, int op2) { return op1 + op2; } },
MINUS { int apply(int op1, int op2) { return op1 - op2; } },
...
;
abstract int apply(int op1, int op2);
}
Случай для EnumMap
Вот цитата из Effective Java 2nd Edition :
Никогда не извлекайте значение, связанное с enum
, из его ordinal()
;вместо этого сохраните его в поле экземпляра.( Элемент 31: Используйте поля экземпляров вместо ординалов ) Редко целесообразно использовать порядковые номера для индексации массивов: используйте EnumMap
вместо.Общий принцип заключается в том, что прикладные программисты должны редко, если вообще когда-либо, использовать Enum.ordinal
.( Элемент 33: используйте EnumMap
вместо порядкового индекса )
По существу, как и раньше, у вас может быть что-то вроде этого:
// BEFORE, with int constants and array indexing
Employee[] employeeOfTheMonth = ...
employeeOfTheMonth[JANUARY] = jamesBond;
Теперь выможет иметь:
// AFTER, with enum and EnumMap
Map<Month, Employee> employeeOfTheMonth = ...
employeeOfTheMonth.put(Month.JANUARY, jamesBond);
Случай EnumSet
Степень двух int
констант часто используется, например, в C ++ для обозначения наборов битов.Это зависит от побитовых операций .Пример может выглядеть примерно так:
public static final int BUTTON_A = 1;
public static final int BUTTON_B = 2;
public static final int BUTTON_X = 4;
public static final int BUTTON_Y = 8;
int buttonState = BUTTON_A | BUTTON_X; // A & X are pressed!
if ((buttonState & BUTTON_B) != 0) { // B is pressed...
...
}
С enum
и EnumSet
это может выглядеть примерно так:
enum Button {
A, B, X, Y;
}
Set<Button> buttonState = EnumSet.of(Button.A, Button.X); // A & X are pressed!
if (buttonState.contains(Button.B)) { // B is pressed...
...
}
Ссылки
См. Также
- Effective Java 2nd Edition
- Элемент 30: используйте
enum
вместо int
константы - Элемент 31: Используйте поля экземпляров вместо ординалов
- Элемент 32: Используйте
EnumSet
вместо битовых полей - Элемент 33: Использовать
EnumMap
вместо порядкового индексирования
Похожие вопросы