这些是主要的论点
enum,
EnumMap以及
EnumSet短的例子。
的情况 enum
从Java
6开始,
java.util.Calendar是一个凌乱类的示例,该类可以从使用中受益匪浅
enum(除其他改进外)。
当前
Calendar定义以下常量(以及许多其他常量):
// int constant antipattern from java.util.Calendarpublic 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 libraryenum Month { JANUARY, FEBRUARY, ...}enum DayOfWeek { SUNDAY, MONDAY, ...}现在我们不必担心
MonDAY ==SUNDAY(它永远不会发生!),并且由于
Month和
DayOfWeek是不同的类型,因此
setMonth(MONDAY)不会编译。
此外,以下是一些前后代码:
// BEFORE with int constantsfor (int month = JANUARY; month <= DECEMBER; month++) { ...}在这里,我们进行各种假设,例如
JANUARY + 1 ==FEBRUARY等等。另一方面,
enum对应项更加简洁,可读性强,并且做出的假设更少(因此,发生错误的机会也更少):
// AFTER with enumfor (Month month : Month.values()) { ...}实例字段的情况
在Java中,
enum是一个
class具有许多特殊的性质,但
class尽管如此,允许您根据需要来定义实例方法和字段。
考虑以下示例:
// BEFORE: with int constantspublic 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 indexingEmployee[] employeeOfTheMonth = ...employeeOfTheMonth[JANUARY] = jamesBond;
现在您可以拥有:
// AFTER, with enum and EnumMapMap<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... ...}参考文献
- Java语言指南/枚举 -包含许多示例的相当完整的介绍
也可以看看
- 有效的Java 2nd Edition
- 条款30:使用
enum
而不是int
常量 - 第31项:使用实例字段代替普通字段
- 项目32:使用
EnumSet
代替位字段 - 项目33:使用
EnumMap
而不是顺序索引
- 条款30:使用



