This enum Goes to Eleven
Although Java syntax borrowed heavily from C++, there are many C++ features that Java chose to omit. At times, the lack of a particular feature makes Java programs cumbersome to implement. One such feature is the ability to define enumerated types, such as those declared with the C++ enum keyword.

An enumerated type holds a set of values defined by the programmer. Usually these values behave much like integers, and as such have an implicit ordering. The principal benefit derived from an enumerated type is the ability to restrict the value of a variable to a discrete set, eliminating the need for explicit range checking and leveraging compiler type-checking for error detection.

You might represent a set of discrete volume levels in C++ as follows:

enum Volume { 
SILENT, SOFT, AUDIBLE, LOUD, ELEVEN };

The most common way to implement this in Java is to define a set of static final integers in an interface and have any class that requires those values either implement the interface or explicitly reference the values. For example:

public interface Volume {
   public static final int SILENT   = 0;
   public static final int SOFT   = 1;
   public static final int AUDIBLE = 2;
   public static final int LOUD   = 3;
   public static final int ELEVEN   = 4;
}

Here any variable of type integer can represent a volume level. We have captured the ordering of the values, but have lost both type safety and the limitation of the domain to a discrete, programmer-defined set. In the C++ example, if a variable is of type Volume, you know that it can only possess one of the five different values allowable to a Volume type. How do we capture this in Java and also retain type safety?

One way to simulate enumerated types in pure object-oriented programming is to declare a new empty class for each enumerated type, and then to create static final members that are instances of the new type and represent the discrete range. Our Java implementation becomes:

Public final class Volume {
   public static final 
      Volume SILENT = new Volume();
   public static final 
      Volume SOFT = new Volume();
   public static final 
      Volume AUDIBLE = new Volume();
   public static final 
      Volume LOUD = new Volume();
   public static final 
      Volume ELEVEN = new Volume();

   private Volume() {
   // Empty private constructor 
   // ensures the only objects of
   // this type are the enumerated 
   // elements declared above.
   }
}

public class Stereo {
    Volume volume = Volume.SILENT;
    ...
}

Defined in this way, a Stereo instance can only have a Volume equal to one of the discrete levels we have defined (and null), but there is no notion of order. Nor can we manipulate the values as integers thereby disallowing their use in a switch statement. Thus a natural enhancement is to add a member variable to Volume defining the ordering of the levels as shown in Listing 1.

This solves the ordering problem and the manipulation of the values as integers. You can determine ordering with equals() and compareTo(). And if you want to use the values in a switch statement, you can fetch a value to test with getLevel() and use the LEVEL constants as the cases.

You may be wondering why we added the getVolume() factory method. If we omit the factory method, a small problem arises with respect to serialization. The Volume class has a private constructor and no setter methods, which would allow us to limit the range of values it can represent. This makes it unserializable. A serializable class must declare Volume variables as transient, and manually serialize the getLevel() value. On deserialization, the class may recreate its Volume member by using the getVolume() factory method. A benefit of this approach is that it allows you to continue to use the == operator for comparison operations, rather than restricting you to invoking equals().

As you can see, it is quite a lot of work to get the functionality of enumerated types in Java. Simply using integer constants will be sufficient for most programs, but when value restriction and type safety become issues, you will likely have to resort to some variation of this more complicated solution.

—Daniel F. Savarese