12 Feb 2008

A better way to use Java Enum than switch

I think java enum are a nice addition to the language. I used to combine them with switch/case statements. But lately François, a colleague, shown we a better way :)

You can embedded behavior in each enumeration case, reducing client code and making it more elegant :)

Let's take a simple example: Every day of the week you need to do something: take out the garbage, go to the laundry etc. ...

Using switch here's how I would have done it:
    private enum Week {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday}

public static void main(String[] args) {
final Week today = getToday();
Action action;
switch (today) {
case Sunday:
action = new FamillyDiner();
break;
case Monday:
case Friday:
action = new Garbage();
break;
case Tuesday:
action = new Laundry();
break;
case Wednesday:
action = new ChillOut();
break;
case Thursday:
action = new Grocery();
break;
case Saturday:
action = new SoccerGame();
break;
default:
throw new RuntimeException("Impossible day!");
}
action.execute();
}

Conclusion:
  • You can see the number of lines in main() is quite important
  • The client code (the switch/case code) is likely to be duplicate
  • There is an unnecessary case "default" that can never happen (even null case would result in a NullPointerException in the switch)
  • But it still much better than a if/elseif/else using int

Adding behavior to the enum:
    private enum Week {
Sunday {
@Override
public void execute() {
System.out.println("Mom and Dad diner.");
}
}, Monday {
@Override
public void execute() {
System.out.println("Take the garbage out.");
}
}, Tuesday {
@Override
public void execute() {
System.out.println("Do the laundry.");
}
}, Wednesday {
@Override
public void execute() {
System.out.println("Have a beer. Just Chill out!");
}
}, Thursday {
@Override
public void execute() {
System.out.println("Go out to the local store for Grocery with shoping list.");
}
}, Friday {
@Override
public void execute() {
System.out.println("Take the garbage out.");
}
}, Saturday {
@Override
public void execute() {
System.out.println("Manchester vs Arsenal! Can't miss that!");
}
};

public abstract void execute();
}

public static void main(String[] args) {
final Week today = getToday();
today.execute();
}

Conclusion:
  • Less lines of code and still readable
  • main() code is soooo easy
  • No unnecessary case "default"
  • If you don't want this behavior and still use the enum you are free to use the switch/case way


Clearly I think this approach is far better and much more elegant unless someone show me otherwise...



Download the complete code sample.

10 comments:

Anonymous said...

that's an interesting approach, but:
- default is optional in a switch
- it's less obvious in your code that 2 days share the same action
- a simple way to make main method cleaner in the switch solution is to create a getAction(Week) factory method.

Benjamin Francisoud said...

Thanks for sharing your remarks :)

- About "default is optional": yes I agree but that doesn't mean it can't happen, whereas with enum that default case can't happen...

- "less obvious code": agreed, it's less obvious. In the enum example, in a real world example, I would have move duplicated code to a separate method (making it a bit more obvious...)

- "create a getAction(Week)": agree too


My point was more about the fact that the switch/case still require more lines of code to do the same as the enum example for the same behavior...

Don't get me wrong the switch/case is perfectly fine and I've been writing such code for a long time. I don't plan to do a search and replace to remove such code in favor of the enum one;)

But I really find the enum example much neater, more elegant and less error prone.

I understand, it's also a matter of taste ;)

Anonymous said...

Interesting article :). Just a short comment :

enumeration should probably be considered as constants.
In order to reuse them, i think, it is advisable to not add business logic in there. BTW find here another interesting article about Java Enumerations, interfaces and internationalization

Anonymous said...

you have a nice site. thanks for sharing this site. there are various kinds of ebooks are available here

http://feboook.blogspot.com

Anonymous said...

Cool stuff. It is kind of condition injection! Do not check for if/else later, build it along with the object. Thanks for sharing.

Онучин Артем said...

Just write now I'm rewriting very similar code from enum to switch style.

I had enum with two abstract methods: one method was used in one class and nowhere else and second method was used in another class and nowhere else.

In this case switch statement is more simple, all code is close. I don't have the entity that does nothing.

So enum is not the best choice in all cases.

Онучин Артем said...

I'm sorry for grammar mistakes: english is not my native laguage...

Singleton in Java said...

I agree Enum has made more power in Java language and thank its not copy paste from other language like C and C++. by using switch with enum makes code a lot cleaner and readable than if-else. You can also see some advanced samples of Enum in Java here.

Nimnio said...
This comment has been removed by the author.
Nimnio said...

Re: Anonymous regarding "enumeration should probably be considered as constants. In order to reuse them, i think, it is advisable to not add business logic in there."

This is confused thinking. Constants can have business logics associated with them. "The basic idea behind Java's enum types is simple: they are classes that export one instance for each enumeration constant via a public static final field" (Bloch). If you think about this for a minute, you will see that an "enum" doesn't do anything you couldn't do with plain classes: in other words, enums are an implementation of a class pattern. While most programmers use enums only to eliminate magic numbers and strings, they are much more flexible and powerful than this.

Re: Онучин Артем regarding "I had enum with two abstract methods: one method was used in one class and nowhere else and second method was used in another class and nowhere else."

This is a general refactoring issue that has nothing to do with enums. You would have experienced the same issue had you put the two unrelated methods in the same class. While it is definitely true that "enum is not the best choice in all cases", the problem you're describing is not a problem with enum's; it is a problem with your code organization.

Great article. I really enjoyed it.