从唯一的实例值中创建枚举工厂方法

时间:2013-01-18 18:01:07

标签: java enums factory

我创建了一个Enum来定义某些动作。针对外部API编程我被迫使用Integer来表达此操作。这就是为什么我在我的Enum中添加了一个整数实例字段。这应该与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语句并添加所有可能的代码并返回适当的常量。但我想避免在两个地方同时定义它们。

3 个答案:

答案 0 :(得分:5)

如果您有很多这样的话,可以使用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查找是O(1)。

答案 1 :(得分: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;
     }
}

答案 2 :(得分:0)

我个人会保持简单(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或查找循环。