使用唯一的实例值创建枚举工厂方法



我已经创建了一个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或查找循环。

相关内容

  • 没有找到相关文章

最新更新