Java Cookbook, Second Edition
Problem
You need to manage a small list of discrete values within a program. Solution
Use the JDK 1.5 enum mechanism. Discussion
To enumerate means to list all the values. You often know that a small list of possible values is all that's wanted in a variable, such as the months of the year, the suits or ranks in a deck of cards, the primary and secondary colors, and so on. The C programming language provided an enum keyword: enum { BLACK, RED, ORANGE} color; Java has been criticized since the earliest releases for its lack of enumerations, which many developers have wished for. Many have had to develop custom classes to implement the "enumeration pattern." But C enumerations are not "typesafe"; they simply define constants that can be used in any integer context. For example, this code compiles without warning, even on gcc 3 with -Wall (all warnings), while a C++[1] compiler catches the error: [1] For Java folks not that familiar with C/C++, C is the older, non-OO language; C++ is an OO derivative of C; and Java is in part a portable, more strongly typesafe derivative of C++. enum { BLACK, RED, ORANGE} color; enum { READ, UNREAD } state; /*ARGSUSED*/ int main(int argc, char *argv[]) { color = RED; color = READ; return 0; } To replicate this mistake in Java, one needs only to define a series of final int values; it will still not be typesafe. By typesafe I mean that you can not accidentally use values other than those defined for the given enumeration. The definitive statement on the "typesafe enumeration pattern" is probably the version defined in Item 21 of Joshua Bloch's book Effective Java (Addison Wesley). Bloch was one of the authors of the Typesafe Enumeration specification for JDK 1.5, so you can be sure the book does a good job of implementing his pattern. These enums are implemented as Classes, subclassed (transparently, by the compiler) from the new class java.lang.Enum . Unlike C, and unlike the "series of final int" implementation, JDK 1.5 typesafe enumerations:
Enum constants are not compiled into clients, giving you the freedom to reorder the constants within your enum without recompiling the client classes. Normally this works correctly and, even if you blow it and remove a constant that a client depends on, you'll get an informative message instead of a cryptic crash. Additionally, an enum type is a class so it can, for example, implement arbitrary interfaces, and you can add arbitrary fields and methods to an enum class. Compared to Bloch's Typesafe Enum pattern in the book:
So there are many benefits and few pitfalls. The new enum keyword is at the same level as the keyword class in declarations. That is, an enum may be declared in its own file with public or default access. It may also be declared inside classes, much like nested or inner classes (see Recipe 9.6). Media.java, shown in Example 8-7, is a code sample showing the definition of a typesafe enum. Example 8-7. Media.java
public enum Media { book, music_cd, music_vinyl, movie_vhs, movie_dvd; } Notice that an enum is a class; see what javap thinks of the Media class: C:> javap Media Compiled from "Media.java" public class Media extends java.lang.Enum{ public static final Media book; public static final Media music_cd; public static final Media music_vinyl; public static final Media movie_vhs; public static final Media movie_dvd; public static final Media[] values( ); public static Media valueOf(java.lang.String); public Media(java.lang.String, int); public int compareTo(java.lang.Enum); public int compareTo(java.lang.Object); static {}; } C:> Product.java, shown in Example 8-8, is a code sample that uses the Media enum. Example 8-8. Product.java
import com.darwinsys.util.Debug; public class Product { String title; String artist; Media media; public Product(String artist, String title, Media media) { this.title = title; this.artist = artist; switch (media) { case book: Debug.println("media", title + " is a book"); break; case music_cd: Debug.println("media", title + " is a CD"); break; case music_vinyl: Debug.println("media", title + " is a relic"); break; case movie_vhs: Debug.println("media", title + " is on tape"); break; case movie_dvd: Debug.println("media", title + " is on DVD"); break; default: Debug.println("media", "Warning: " + title + ": Unknown media " + media); break; } this.media = media; } } In Example 8-9, MediaFancy shows how operations (methods) can be added to enumerations; the toString( ) method is overridden for the "book" value of this enum. Example 8-9. MediaFancy.java
public enum MediaFancy { book { public String toString( ) { return "Book"; } }, music_cd, music_vinyl, movie_vhs, movie_dvd; public static void main(String[] args) { MediaFancy[] data = { book, movie_dvd, music_vinyl }; for (MediaFancy mf : data) { System.out.println(mf); } } } Running the MediaFancy program produces this output: Book movie_dvd music_vinyl That is, the Book values print in a "user-friendly" way compared to the default way the other values print. You'd want to extend this to all the values in an Enumeration. Finally, EnumList, in Example 8-10, shows how to list all the possible values that a given enum can take on; simply iterate over the array returned by the class's values( ) method. Example 8-10. EnumList.java
/** Simple demo to print all the types of an enum. */ public class EnumList { public static void main(String[] args) { enum State { ON, OFF, UNKNOWN }; for (State i : State.values( )) { System.out.println(i); } } } The output of the EnumList program is, of course: ON OFF UNKNOWN |