Say, you have an enum representing a state:
enum State { A, B, C, D; }
And you want to know if a state is a final state. In our example C and D should be final.
An initial attempt might be to use a simple method:
public boolean isFinal() { return State.C == this || State.D == this; }
When there are two states this might seem reasonable but adding more states to this condition makes it unreadable pretty fast.
So why not use the enum hierarchy?
A(false), B(false), C(true), D(true); private boolean isFinal; private State(boolean isFinal) { this.isFinal = isFinal; } public boolean isFinal() { return isFinal; }
This was and is in some cases a good approach but also gets cumbersome if you have more than one attribute in your constructor.
Another attempt I’ve seen:
public boolean isFinal() { for (State finalState : State.getFinalStates()) { if (this == finalState) { return true; } } return false; } public static List<State> getFinalStates() { List<State> finalStates = new ArrayList<State>(); finalStates.add(State.C); finalStates.add(State.D); return finalStates; }
This code gets one thing right: the separation of the final attribute from the states. But it can be written in a clearer way:
List<State> FINAL_STATES = Arrays.asList(C, D) public boolean isFinal() { return FINAL_STATES.contains(this); }
Another common problem with enums is constructing them via an external representation, e.g. a text.
The classic dispatch looks like this:
public static State createFrom(String text) { if ("A".equals(text) || "FIRST".equals(text)) { return State.A; } else if ("B".equals(text)) { return State.B; } else if ("C".equals(text)) { return State.C; } else if ("D".equals(text) || "LAST".equals(text)) { return State.D; } else { throw new IllegalArgumentException("Invalid state: " + text); } }
Readers of refactoring sense a code smell here and promptly want to refactor to a dispatch using the hierarchy.
A("A", "FIRST"), B("B"), C("C"), D("D", "LAST"); private List<String> representations; private State(String... representations) { this.representations = Arrays.asList(representations); } public static State createFrom(String text) { for (State state : values()) { if (state.representations.contains(text)) { return state; } } throw new IllegalArgumentException("Invalid state: " + text); }
Much better.