Neater Java
In Java 5, as all good Java developers will know, Sun introduced the enum type. Since I’m sure anyone who is interested in what will follow knows that already, I won’t bore you with the details but there’s some decent historical information on Sun’s reasons available. I’m going to be talking a little bit about operations on enums, so let’s get started by defining one:
public enum OrderState{
PENDING, CHECKING_CREDIT, PROCESSING, SHIPPED, RECEIVED, CANCELLED, RETURNED
}Each of those items in the enumeration represent some state that an order can be in; an order being anything you can buy from an online store. We’re also going to have a variable called currentState which will hold one of these enumerated items. Let’s not think too hard about how accurate the model is, it’s really just there as a simple example.
Now imagine that one of our requirements is to enable a button if the currentState is one of several possible states, but not in any other state. For example, the “Cancel” button is enabled while the current state is PENDING, PROCESSING, or CHECKING_CREDIT. We’re doing a containment check, a pretty common operation. The standard Java approach would be:
if(currentState.equals(Order_State.PENDING)|| currentState.equals(Order_State.PROCESSING)|| currentState.equals(Order_State.CHECKING_CREDIT)){
cancelButton.enable();
}Yuck! It gets the job done but that is some verbose and ugly code. We know that there’s got to be a better way, and there is: the EnumSet class. EnumSet is a somewhat overlooked Set implementation, specifically for grouping together items from the same Enum. I’ll let you look into the details of the API, but there are some very handy methods, such as range() and complementOf(). As a bonus, they’re also very efficient. How would our example look using an EnumSet?
if(EnumSet.of(Order_State.PENDING, Order_State.PROCESSING, Order_State.CHECKING_CREDIT).contains(currentState)){
cancelButton.enable();
}That’s much better. Rather than a bunch of ORs that look disconnected and have a lot of repeated syntax, we state pretty clearly what we want: build a set of states, and then see whether the current state is part of that set.
There is another neater way of doing this, purely in terms of how syntactically nice it looks, though it requires a little helper function in the Enum. Add the following in() method directly to the Enum type:
public boolean in(Order_State... args){
return EnumSet.copyOf(Arrays.asList(args)).contains(this);
}Now, we can do our contains check like this:if(currentState.in(Order_State.PENDING, Order_State.PROCESSING, Order_State.CHECKING_CREDIT)){
cancelButton.enable();
}Very tidy: we’ve got a single method call that expresses exactly what we’re aiming for in a fluent manner. Now, it does have it’s downsides like the helper function in the enum, and the fact that EnumSet is more efficient. I’d suggest, though, that in 99% of cases you’re not going to be caring about those few extra nanoseconds required to convert to a List first, and that the few seconds of developer time you save every time someone has to read and understand each version more than makes up for it.
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)





Comments
Mahmoud Gangat replied on Thu, 2008/08/14 - 3:43am
You could also do this to skip the list conversion:
public boolean in(Order_State arg, Order_State... args){return EnumSet.of(arg,args).contains(this);
}
replied on Thu, 2008/08/14 - 5:48am
I guess there is no need to do that all every time (assuming the condition does not change dynamically):
static EnumSet cancellableStates = EnumSet.of(OrderState.PENDING, OrderState.PROCESSING, OrderState.CHECKING_CREDIT);
...
if(cancellableStates.contains(currentState)){
cancelButton.enable();
}
Jeroen van Bergen replied on Thu, 2008/08/14 - 10:10am
Why not add a little more behaviour to the OrderState to enforce that only valid state transitions are allowed:
public OrderState transitionTo(OrderState requestedState) {switch (this) {
case PENDING: {
if (requestedState == PROCESSING || requestedState == CANCELLED) {
return requestedState;
}
//....
}
}
}
A big switch statement like this is not very pleasant to see, but using it ensures you can only perform the transitions that are valid given the state/transition model that is implemented in a single place.
Eric Jablow replied on Thu, 2008/08/14 - 10:28am
If it is important to distinguish between cancellable and non-cancellable states, make cancellable an instance variable in the enumeration:
public enum OrderState{PENDING(true), CHECKING_CREDIT(true), PROCESSING(true),
SHIPPED(false), RECEIVED(false), CANCELLED(false), RETURNED(false);
private OrderState(boolean cancellable) {this.cancellable = cancellable;
}
public boolean isCancellable() { return cancellable; }}
Torbjörn Gannholm replied on Thu, 2008/08/14 - 5:11pm
Steven Baker replied on Thu, 2008/08/14 - 6:10pm
first of all, dont write:
you should write:
this is to avoid possible NPEs
and why is OrderState sometimes named: Order_State???
Victor Tsoukanov replied on Fri, 2008/08/15 - 1:44am
It is just a remark, Component.enable() had been deprecated and usage of Component.setEnabled(boolean) looks better
James Selvakumar replied on Mon, 2008/08/18 - 5:17am
Nice article. Thanks for highlighting this.
christiaan_se replied on Mon, 2008/08/18 - 9:18am
Also realize that the static constructor (strangely) doesn't allow for null values or empty lists, so you might want to add a check around this:
public boolean in(Order_State... args){ if(args.length == 0) { //just to show construction of empty EnumSet, always returns false return EnumSet.noneOf(Order_State.class).contains(this); } return EnumSet.copyOf(Arrays.asList(args)).contains(this); }Milos Ufak replied on Wed, 2008/08/20 - 12:16am
if(EnumSet.of(Order_State.PENDING, Order_State.PROCESSING, Order_State.CHECKING_CREDIT).contains(currentState)){
cancelButton.enable();
}
is smart. Interesting idea for me. But version vith in() method is unefficient because returns always new object (does JVM it optimize? ) . Simple if has constant object., I think.
Milos Ufak replied on Wed, 2008/08/20 - 3:44pm
Gauthier, Hough... replied on Thu, 2011/08/04 - 1:14pm