我已经创建了一个Enum来定义某些操作。针对外部API编程,我被迫使用Integer
来表示此操作。这就是为什么我在枚举中添加了一个整数实例字段。这应该与Joshua Bloch的Effective Java一致,而不是依赖于ordinal()
或使用values()[index]
的Enum常量顺序。
public enum Action {
START(0),
QUIT(1);
public final int code;
Protocol(int code) {
this.code = code;
}
}
我从API得到一个整数值what
,现在我想创建一个枚举值出来,我怎么能以最通用的方式实现这一点?
显然,添加这样一个工厂方法是行不通的。不能实例化Enum。
Action valueOf(int what) {
return new Action(what);
}
当然,我总是可以创建一个switch-case语句,添加所有可能的代码并返回适当的常量。但是我想避免在两个地方同时定义它们
如果你要有很多,你可以使用HashMap<Integer, Action>
:
private static final Map<Integer, Action> actions = new HashMap<>(values().size, 1);
static {
for (Action action : values())
actions.put(action.code, action);
}
// ...
public static Action valueOf(int what) {
return actions.get(what);
}
这是有用的,如果你要有大量的Action
值,因为HashMap
查找是0(1)。
如果你确定你的代码总是顺序的,并且从0开始,那么最有效的选择将是
public enum Action {
START(0),
QUIT(1);
public static final Action[] ACTIONS;
static {
ACTIONS = new Action[values().length];
for(Action a : values()) {
ACTIONS[a.code] = a;
}
}
public final int code;
Protocol(int code) {
this.code = code;
}
}
我个人会保持简单(YAGNI)并使用序数值,但是:
- 我会保持逻辑内枚举,以确保外部代码不知道的实现细节,不依赖于它
- 我会确保我有一个测试失败,如果有什么东西打破(即,如果数字不从0开始或不是增量)
枚举代码:
public enum Action {
START(0),
QUIT(1);
private final int code;
Action(int code) {
this.code = code;
}
public int getCode() {
return code;
}
public static Action of(int code) {
try {
return Action.values()[code];
} catch (IndexOutOfBoundsException e) {
throw new IllegalArgumentException("not a valid code: " + code);
}
}
}
@Test
public testActionEnumOrder() {
int i = 0;
for (Action a : Action.values()) {
assertEquals(a.getCode(), i++);
}
}
如果将QUIT(1)
更改为QUIT(2)
,则测试将失败。当这种情况发生时,您可以使用HashMap或查找循环。