Это основные аргументы в пользу _1 _, EnumMap
и _ 3_ короткими примерами.
Дело для 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
, хотя они, очевидно, представляют разные концептуальные сущности.
Ниже приведены некоторые серьезные последствия:
- It's brittle; care must be taken to assign different numbers whenever needed.
- If by mistake we set
MONDAY = 0;
, SUNDAY = 0;
, then we have MONDAY == SUNDAY
- There is no namespace and no type-safety, since everything is just an
int
:
- We can
setMonth(JANUARY)
, but we can also setMonth(THURSDAY)
or setMonth(42)
- Кто знает, что такое _ 17_ (настоящий метод!) Работает!
Напротив, вместо этого у нас могло бы быть что-то вроде этого:
// 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
, из его _ 39_; вместо этого сохраните его в поле экземпляра. (Правило 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
- Item 30: Use
enum
instead of int
constants
- Правило 31: используйте поля экземпляра вместо порядковых номеров
- Правило 32: Используйте
EnumSet
вместо bit поля
- Правило 33: Используйте
EnumMap
вместо порядкового номера индексация
Связанные вопросы
person
polygenelubricants
schedule
29.07.2010
enum
- мощная абстракция, особенно в Java, чем в C # / C ++. - person polygenelubricants   schedule 29.07.2010