3.8 Enumerated Types
In this section we provide a basic introduction to enumerated types. An enumerated type defines a finite set of symbolic names and their values. These symbolic names are usually called enum constants or named constants.
One way to define constants is to declare them as final, static variables in a class (or interface) declaration:
public class MachineState { public static final int BUSY = 1; public static final int IDLE = 0; public static final int BLOCKED = -1; }
Such constants are not type-safe, as any int value can be used where we need to use a constant declared in the MachineState class. Such a constant must be qualified by the class (or interface) name, unless the class is extended (or the interface is implemented). When such a constant is printed, only its value (for example, 0), and not its name (for example, IDLE), is printed. A constant also needs recompiling if its value is changed, as the values of such constants are compiled into the client code.
An enumerated type in Java is a special kind of class type that is much more powerful than the approach outlined earlier for defining collections of named constants.
Declaring Type-safe Enums
The canonical form of declaring an enum type is shown here:
public enum MachineState // Enum header { // Enum body BUSY, IDLE, BLOCKED // Enum constants }
The keyword enum is used to declare an enum type, as opposed to the keyword class for a class declaration. The basic notation requires the enum type name in enum header, and a comma-separated list of enum constants can be specified in the enum body. Optionally, an access modifier can also be specified in the enum header, as for a (top-level) class. In the example enum declaration, the name of the enum type is MachineState. It defines three enum constants with explicit names. An enum constant can be any legal Java identifier, but the convention is to use uppercase letters in the name. Essentially, an enum declaration defines a reference type that has a finite number of permissible values referenced by the enum constants, and the compiler ensures they are used in a type-safe manner.
Other member declarations can be specified in the body of an enum type, but the canonical form suffices for the purpose of this book. Analogous to a class declaration, an enum type is compiled to Java bytecode that is placed in a separate class file.
The enum types java.time.Month and java.time.DayOfWeek are two examples of enum types from the Java SE platform API. As we would expect, the Month enum type represents the months from JANUARY to DECEMBER, and the DayOfWeek enum type represents the days of the week from MONDAY to SUNDAY. Examples of their usage can be found in §11.2, p. 462.
Some additional examples of enum types follow:
public enum MarchingOrders { LEFT, RIGHT } public enum TrafficLightState { RED, YELLOW, GREEN } enum MealType { BREAKFAST, LUNCH, DINNER }
Using Type-safe Enums
Example 3.13 illustrates the use of enum constants. An enum type is essentially used in the same way as any other reference type. Enum constants are actually public, static, final fields of the enum type, and they are implicitly initialized with instances of the enum type when the enum type is loaded at runtime. Since the enum constants are static members, they can be accessed using the name of the enum type—analogous to accessing static members in a class or an interface.
Example 3.13 shows a machine client that uses a machine whose state is an enum constant. In this example, we see that an enum constant can be passed as an argument, as shown as (1), and we can declare references whose type is an enum type, as shown as (3), but we cannot create new constants (that is, objects) of the enum type MachineState. An attempt to do so, at (5), results in a compile-time error.
The string representation of an enum constant is its name, as shown at (4). Note that it is not possible to pass a type of value other than a MachineState enum constant in the call to the method setState() of the Machine class, as shown at (2).
Example 3.13 Using Enums
// File: MachineState.java public enum MachineState { BUSY, IDLE, BLOCKED }
// File: Machine.java public class Machine { private MachineState state; public void setState(MachineState state) { this.state = state; } public MachineState getState() { return this.state; } }
// File: MachineClient.java public class MachineClient { public static void main(String[] args) { Machine machine = new Machine(); machine.setState(MachineState.IDLE); // (1) Passed as a value. // machine.setState(1); // (2) Compile-time error! MachineState state = machine.getState(); // (3) Declaring a reference. System.out.println( "Current machine state: " + state // (4) Printing the enum name. ); // MachineState newState = new MachineState(); // (5) Compile-time error! System.out.println("All machine states:"); for (MachineState ms : MachineState.values()) { // (6) Traversing over enum System.out.println(ms + ":" + ms.ordinal()); // contants. } System.out.println("Comparison:"); MachineState state1 = MachineState.BUSY; MachineState state2 = state1; MachineState state3 = MachineState.BLOCKED; System.out.println(state1 + " == " + state2 + ": " + (state1 == state2)); // (7) System.out.println(state1 + " is equal to " + state2 + ": " + (state1.equals(state2))); // (8) System.out.println(state1 + " is less than " + state3 + ": " + (state1.compareTo(state3) < 0)); // (9) } }
Output from the program:
Current machine state: IDLE All machine states: BUSY:0 IDLE:1 BLOCKED:2 Comparison: BUSY == BUSY: true BUSY is equal to BUSY: true BUSY is less than BLOCKED: true
Selected Methods for Enum Types
All enum types implicitly have the following useful method:
The loop at (6) in Example 3.13 illustrates traversing over all the MachineState enum constants in the order they are specified. An array containing all the MachineState constants is obtained by calling the static method values() on the enum type.
All enum types are subtypes of the java.lang.Enum class, which provides the default behavior. All enum types inherit the following selected methods from the java.lang.Enum class:
Note that the equality test implemented by the equals() method is based on reference equality (==) of the enum constants, not on value equality. An enum type has a finite number of distinct objects. Comparing two enum references for equality means determining whether they store the reference value of the same enum constant—in other words, whether the references are aliases. Thus, for any two enum references state1 and state2, the expressions state1.equals(state2) and state1 == state2 are equivalent, as shown at (7) and (8) in Example 3.13.
The ordinal value of the constants in an enum type determines the result of comparing such constants with the compareTo() method, as shown at (9) in Example 3.13.