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