Typesafe Enum: Using enum in J2SE 5.0

by Jeff Langr

January 20, 2004

Originally posted 12-Nov-2003

Java version 5.0 introduces many dramatic new language features. This article discusses the new enum facility.

Enumerated Constants

A frequent pattern in Java is the use of enumerated constants–that is, a set of static final variables that can be semantically grouped. For example, a student grading system needs to be able to capture each of five possible letter grades–A, B, C, D, or F. One standard idiom for implementing this list of constants in Java:

    public class Student {
       public static final char GRADE_A = 'A';
       public static final char GRADE_B = 'B';
       public static final char GRADE_C = 'C';
       public static final char GRADE_D = 'D';
       public static final char GRADE_F = 'F';
       // ...
       public void addGrade(char grade) {
       	// ...
       }
    }

Client code:

    new Student().addGrade(Student.GRADE_B);

There are a few problems. First, the declaration code is redundant–public static final this, public static final that, etc. Secondly, multiple classes may have use for the constant, but not for the class in which they are defined. Third, and most problematic, is that the constants are not type-safe: Nothing prohibits a client from passing in the character 'Z' to the addGrade method.

The second problem, where the constants are defined, is often solved by use of the Constant Interface pattern. A Constant Interface takes advantage of the fact that you can declare static constants in a Java interface. Classes that need to use the constants simply implement the interface.

However, Joshua Bloch, in his book Effective Java, describes Constant Interface as an anti-pattern. It violates good coding standards by exposing an implementation detail–where constants are defined–to client code. A downside of doing so is that you cannot change the constant interface without impacting the client.

Typesafe Enum

As an alternative, Bloch and I (see Essential Java Style) have promoted the Typesafe Enum pattern.

Using Typesafe Enum, you create a new class whose constructor is private.
You create static constants of the class to represent the only valid
instances. This restricts clients to only use the valid instances.

Building a Typesafe Enum class is not a huge task, but nor is it trivial. Getting nuances such as serialization and synchronization capability working right can be difficult. Thus Bloch was the main promoter of building a correct implementation of Typesafe Enum directly into the Java language.

The enum Type

J2SE 5.0 allows you to declare a type as an enum type. In its simplest form, an enum type requires you to specify a type name and the names of its valid instances. The declaration is about as succinct as possible:

    enum Grade { A, B, C, D, F };

That’s it. An enum can be a top-level type, or it can be declared within another class. You use the enum instances just as you would refer to a static variable:

    Grade grade = Grade.C;

The addGrade method declaration changes to:

    public void addGrade(Grade grade)

It is no longer possible to pass an invalid parameter! The only valid Grade instances are defined by the Grade enum itself. (It is also not possible to subclass the Grade enum, one technique which would have provided a workaround to the safety mechanism.)

Switching on enum Values

The switch statement has been enhanced to support enum types:

    switch (grade) {
       case A: print("great"); break;
       case B: print("good"); break;
       case C: print("ok"); break;
       case D: print("eh"); break;
       case F: print("loser"); break;
    }

However, as per good OO programming guidelines, you should avoid the use of switch statements. One way would be to provide a mechanism in the Grade type itself to return an appropriate message. An enum type can have fields, constructors, and methods, just like a class type. Here’s how you could associate a message with each Grade instance:

    enum Grade {
       A("great"),
       B("good"),
       C("ok"),
       D("eh"),
       F("loser");
    
       private String message;
    
       Grade(String message) {
          this.message = message;
       }
    
       String getMessage() {
          return message;
       }
    }

An example of printing the message:

    Grade grade = Grade.C;
    System.out.println(grade.getMessage());

Additional Fields

There are three useful methods that you can use with enum types. First, the static method values() returns a list of all the enum constants. The following code uses the J2SE 5.0 for-each loop to iterate the list of Grade instances and print each:

    for (Grade grade: Grade.values())
       System.out.println(grade);

You might also find the enum public instance methods ordinal() and name() useful. The name() method returns the name of the instance as specified in the enum declaration. For example, the name for Grade.C is “C”. The ordinal() method returns an int representing the position of the Grade instance in the enum list. Thus the ordinal of Grade.A is 0, the ordinal of Grade.B is 1, and so on. Sun recommends you not depend on the value of the ordinal.

Since each enum has an associated name, the Enum class provides a way for you to get the appropriate enum instance given a String.

    Grade grade = Grade.valueOf("F");

You’ll get an IllegalArgumentException if you pass a String that doesn’t match any of the enum names.

Polymorphic Enums

The follow-up article, Polymorphic Enums in J2SE 5.0 (Tiger), discusses how you can use polymorphism to create distinct behavior for any enum constant.

Comments

Pingback: Langr Software Solutions » Polymorphic Enums in J2SE 5.0

Share your comment

Jeff Langr

About the Author

Jeff Langr has been building software for 40 years and writing about it heavily for 20. You can find out more about Jeff, learn from the many helpful articles and books he's written, or read one of his 1000+ combined blog (including Agile in a Flash) and public posts.