Create a enum factory method out of a unique instance value

為{幸葍}努か 提交于 2019-12-23 10:56:24

问题


I have created an Enum to define certain actions. Programming against a external API I am forced to use an Integer to express this action. That's why I have added an integer instance field to my Enum. This should be d'accord with Joshua Bloch's Effective Java, instead of relying on ordinal() or the order of the Enum constants using values()[index].

public enum Action {

    START(0),

    QUIT(1);

    public final int code;

    Protocol(int code) {
         this.code = code;
     }
}

I get an integer value what from the API and now I want to create an Enum value out of it, how can I implement this in the most generic fashion?

Obviously, adding such a factory method, will not work. You cannot instantiate an Enum.

Action valueOf(int what) {
     return new Action(what);
}

Of course, I can always make a switch-case statement and add all the possible codes and return the appropriate constant. But I want to avoid defining them in two places at the same time.


回答1:


If you're going to have a lot of them, you can use a 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);
}

This is useful if you're going to have a large number of Action values since the HashMap lookup is O(1).




回答2:


If you are sure that your codes will always be sequential and starting from 0 then the most efficient option would be

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;
     }
}



回答3:


I would personally keep it simple (YAGNI) and use the ordinal value but:

  • I would keep the logic within the enum to make sure outside code does not know about that implementation detail and does not rely on it
  • I would make sure I have a test that fails if something breaks (i.e. if the numbers don't start from 0 or are not incremental)

enum code:

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

@Test
public testActionEnumOrder() {
    int i = 0;
    for (Action a : Action.values()) {
        assertEquals(a.getCode(), i++);
    }
}

If you change QUIT(1) to QUIT(2) for example, the test will fail. When that happens, you can use a HashMap or a lookup loop.



来源:https://stackoverflow.com/questions/14404840/create-a-enum-factory-method-out-of-a-unique-instance-value

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!