Making the Most of Java 5.0: Enum Tricks

Tagged:  

So your organization has finally moved to Java 5.0...and you are wondering what you can do with some of its new features. In this first of a series of articles on Java 5.0, I'll show you some of the interesting ways that you can exploit the new language features to improve the way you code Java.

First off, enumerations.

Introduction to Enums

The enumeration is a way of creating self-documenting code, a big improvement over the "magic" integer or string constants which are often used to represent a defined set of values. Enumerations abound in object models, and examples are not hard to find: days of the week, colors, card suits, etc. The idea of enumerations as an actual programming language type goes back to Pascal. Many programmers, though, are likely to be more familiar with the C/C++ version of it as the enum, which is just a decorated integer:

//use optional assignment to ints to illustrate
enum suit {
   SPADES = 1,
   HEARTS = 2,
   DIAMONDS = 3,
   CLUBS = 4
};

Figure 1: C/C++ enum

Enums in Java

In Java, the introduction of enumerations started informally with Joshua Bloch's type-safe enumeration pattern. The pattern boils down to essentially this: a Java class with a private constructor that declares public static final versions of itself.

public final class Suit {
     private Suit() {}
     public static final Suit SPADES = new Suit();
     public static final Suit HEARTS = new Suit();
     public static final Suit DIAMONDS = new Suit();
     public static final Suit CLUBS = new Suit();
}

Figure 2: The type-safe enumeration pattern

The advantage of this style of enumeration over the C/C++-style enum or constants is that they are "type-safe", meaning that, for example, if you define a method

public void setSuit(Suit suit) { ... }

the caller cannot pass in a value that does not correspond to an enumeration value. With a constant or C/C++-style enum, you could pass in a random value (like -1) that would not. Java-style enumerations have the additional advantage that, since they are classes, you can "associate" other data with them. This idea will be illustrated a little later. Since these enumeration instances are all effectively singletons, they can be compared for equality using identity ("==").

Enums in Java 5.0

In Java 5.0, the enum keyword is introduced as a special type of class that always extends java.lang.Enum. These closely resemble an abbreviated version of Bloch's type-safe enumeration pattern.

public enum Suit {
     SPADES,
     HEARTS,
     DIAMONDS,
     CLUBS        //optionally can end with ";"
}

Figure 3: The Java 5.0 enum

The difference is that this version includes utility methods like name() that returns the declared variable name as a String, ordinal() that returns the position of the enum as it is declared in the class, and valueOf() which allows you to look up the enum using its declared variable name as a String. Java 5.0 enumerations also can be used in switch statements, a nice convenience. Note that the enumeration values are still static class members, though not declared as such.

So, going beyond the basics, what can you do with Java enumerations? Here are a few handy tricks...

Enums and Template Methods

Remember that the enum is basically a special class type, and can have methods and fields just like any other class. Remember also that each declared field is also an instance of the enum. So, applying the "Template Method" design pattern, one can create enumerations that are factories or command objects, but a defined set of objects. Here is a simple example of a "command" enumeration:

public enum Toy {
     DOLL() {
          @Override public void execute() { 
               System.out.println("I'm a doll."); 
          }
     },
     SOLDIER() {
          @Override public void execute() { 
               System.out.println("I'm a soldier."); 
          }
     };
     //template method
     public abstract void execute();
}

Figure 4: The enum with template method

With the use of static imports, the client code calling this enumeration would look like:

SOLDIER.execute();
DOLL.execute();
//or better...
getToy().execute();

The resulting code is clear and self-documenting. Using this pattern is a great alternative in many cases to the more common if(toy=="soldier"){...} else if(toy=="doll"){...} else{...} logic since it is easier to read, extend, and maintain.

Reverse Lookups

Often in your object model it is common to have data that is naturally "associated" with an enumeration. Since an enum is a class, it is easy to represent this associated information as class fields. Often it is desirable to "lookup" the associated enumeration using the field value. This is easy to do using a static java.util.Map. Take, for example, a Status enum that has an associated status code.

public enum Status 
{
     WAITING(0),
     READY(1),
     SKIPPED(-1),
     COMPLETED(5);

     private static final Map<Integer,Status> lookup 
          = new HashMap<Integer,Status>();

     static {
          for(Status s : EnumSet.allOf(Status.class))
               lookup.put(s.getCode(), s);
     }

     private int code;

     private Status(int code) {
          this.code = code;
     }

     public int getCode() { return code; }

     public static Status get(int code) { 
          return lookup.get(code); 
     }
}

Figure 5: The enum reverse lookup

The static get(int) method here provides the reverse lookup by simply getting the value from the Map. The static block to populate the Map uses a specialized implementation of Set, java.util.EnumSet, that "probably" (according to the javadocs) has better performance than java.util.HashSet. Java 5.0 also provides java.util.EnumMap, a specialized implementation of Map for enumerations that is more compact than java.util.HashMap.

Conclusion

The enum in Java 5.0 represents a cleaner, more self-documenting, richer, and in many cases, more efficient way of coding certain object model concepts than was possible using the older idiom of static final constants. Even if you are still using Java 1.4.x or earlier, you can still gain many of the benefits of the enum by using the type-safe enumeration pattern; this will also make the code easier to migrate once you do go to Java 5.0. Older client code that uses constants can still be adapted to use the enum by associating constant values with the enumerations themselves (as seen in the Status class, Figure 5). So, in other words, there is no reason not to convert your old static constants to enumerations. The guy who has to maintain your code will thank you.

Update: there's a second part to this article here.

[...] a series of articles Brennan Spies explores some of the Java 5 enhancements, generics, enums and [...]

[...] introductions to a concept, but often tend to lack much depth. So, as a follow-up to the original article on enums in Java 5.0, this article goes into a little more depth with a real-world [...]

[...] introductions to a concept, but often tend to lack much depth. So, as a follow-up to the original article on enums in Java 5.0, this article goes into a little more depth with a real-world [...]

good post. keep going

I love especially the reverse lookup part. Until now, I've been using separate HashMaps, but your solution is much slicker. Thanks!

This is article Simply superb and easy understandable of different EnumTypes in java.

Thanks
Narasimha

I've just started to use 1.5 features, and have a few days using enums, but don't knew they were so powerful.

Thanks a lot.

[...] Making the Most of Java 5.0: Enum Tricks [...]

Loved the lookup solution - I was just looking for something like this.
You saved me prbably an hour so I can go out now and enjoy sunshine :D

Thanks a lot !

BTW you've missed a wonderful feature of enums: they can implement interfaces. So if you want an interface with only a set of predefined objects available this is the solution.

Ah Great!!! Easy way to get this done Reverse Loop, i have noted down the Trick, this will save my 1 hour as well. Thanks a again :)

Your a GENIUS!

Excellent article.

What is
WAITING(0),
READY(1),
..

really?

Is it like
public final static WAITING = new Status( 0 );
...

?

Yes, precisely. It is a constructor argument. The equivalent of:
public final static Status WAITING = new Status(0);
as you said.

Will replacing

static {
for(Status s : EnumSet.allOf(Status.class))
lookup.put(s.getCode(), s);
}
private Status(int code) {
this.code = code;
}
...

by

private Status(int code) {
this.code = code;
lookup.put( code, this );
}
...

also work?

No, you can't. You will get a compiler error: "Cannot refer to the static enum field Status.lookup within an initializer".

When I try to compile your example,
the compiler says

"modifier final not allowed here"

public final enum Status

????

@John,

Yes, you're right (the final keyword should not be there). Enums are effectively final (you cannot extend them with other enums), but only after they have been subclassed by internal implementations of the class (which is why you are able to use the template method). I've edited the example with the correction.

Yeah the Java Enum is awesome. Just too bad you can not model self-relations (with the Sun compiler anyway).

@Casper,

Yeah, being able to model, e.g., a state machine with an enum would be very cool. If I had my choice, though, I would have enums that can (optionally) be some other type, e.g. an enum of Strings. Hence they could function like restrictions in XSD.


enum TestEnum{
ENUM_CONST1{

public void testMethod(){
System.out.println("from test method");
}

};
}

As written above, ENUM_CONST1 should be an instance of TestEnum which is some kind of class.
Any ideas why this code won't work ?

public static void main(String[] args) {

TestEnum.ENUM_CONST1.testMethod()

}

From 8.9 of the language specification:

Instance methods declared in these class bodies are may be invoked outside the enclosing enum type only if they override accessible methods in the enclosing enum type.

The reason is simple: a SomeEnum.SOME_ENUM enum returns to the LHS a reference to the enclosing enum class and not the anonymous inner sub-class that is created with each enum instance. Hence you must have the method declared in the body of the enum class to be referenceable in this case.

Try declaring testMethod() as a TestEnum method stub and then overriding it in ENUM_CONST1

NB. I have not personally tested the validity of this suggestion due to a lack of a ready compiler but intuition tells me this is the natural extension (of the previous poster's explaination) that should produce the effect you desired.

public enum Abc {
CDE {
public String testMethod() {
return "Hello enumerated world!";
}
public String toString(){
return testMethod();
}
};
};

and...

import static Abc.*;
public class Test {
public static void main(String[] args){
CDE.toString();
}
}

gives the result of:
Hello enumerated world!

FYI. Your Toy enum above is not valid Java and does not compile.

Yes it does. Did you copy the code correctly?

It's spelled "suit" not "suite."

Thanks a lot, the lookup mechanism is what I had search for!

Informative and cleanly written. Thanks!

p.s. As-is, Toy compiles and runs just fine.

What a great tip! Very useful and elegant. Thanks a lot for contributing...

[...] http://www.ajaxonomy.com/2007/java/making-the-most-of-java-50-enum-trick...   LikeBe the first to like this post. [...]

for(Status s : EnumSet.allOf(Status.class))
should be written as
for(Status s : Status.values())

Java is the most important language now-a-days in web development..
It is used worldwide on a large scale..
Creating custom Enums in Java is a great boon.. Also the templates are of a great use..
Thanks for this information..
It will definitely help Java Developers on a large scale.

Very nice and self explanatory article, was reading quite a few article on it but this is best one.
You can go over on these articles Enum Examples or Enum in java

nice post with good amount of information.
enum can be declared in own class or inside other class, you can also see one of best article about java enum examples

Indeed Enum are more versatile than one can think of , see the below post 10 examples of Enum in java to know what else can you do with Enum in java.

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <pre> <div> <blockquote> <object> <embed> <img> <param>
  • Lines and paragraphs break automatically.
  • Web page addresses and e-mail addresses turn into links automatically.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
Copy the characters (respecting upper/lower case) from the image.