Enums was introduced in the Java language as part of its version 5 release. Enum stands for enumeration, which literally means a numbered list. Other Object-Oriented languages, such as C++, has this feature for a long time. But, Java made it more powerful and robust. It came a much longer way than just representing a list of numbers. This article goes into the details of enum and how it is to used effectively in Java.
What Is Enum?
The idea of enum is simple in the sense that it represents a set of fixed or constant values. It is ideal for designing a list which has a constant set of states, such as the days of the weeks or a list of month names, and so forth. Although they are represented as a set of names, they actually are constant set of numeric values. They can be treated as a special metadata associated with different elements and constructs of the language. This idea of enum eliminated the need of having many boilerplate XML descriptors that existed in Java programming. Enum incorporated a type-safe and new way to our configuration and customization needs.
Enum as a Class
A typical way to model a set of fixed values in Java is to declare the field as final and static.
public class WeekDays{ public static final int SUNDAY = 0; public static final int MONDAY = 1; public static final int TUESDAY = 2; public static final int WEDNESDAY = 3; public static final int THURSDAY = 4; public static final int FRIDAY = 5; public static final int SATURDAY = 6; }
This definitely is a solution, but it is not an ideal one because these constants have a general data type like any other instance field of a class. As a result, if we use them, we need to document and assert their true meanings at every place we use these constants. This is also not a type-safe representation of the idea that we actually need, because we always can use an integer to mean the constant, be it intentionally or unintentionally, and do something that does not make any sense.
public class WeekDays { // ... public int doSomeNonSense(int data) { return FRIDAY*THURSDAY-data; } }
The Java compiler raises no issues with such semantically wrong statements because they are logically and syntactically correct statements. This is a solution in Java only for versions prior to 5. Now, we can use enum to mean the same thing, as follows:
public enum WeekDays { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY }
It is like a class with the values listed in the enum definition. The Java compiler assures that it will do the type checking and we cannot resort to any nonsensical statement or mischief. Note that it is but a convention and not a compiler injunction to use UPPERCASE in enum.
Extensible Enum
Enum, as a specialized class, can be extended to have instance fields, constructors, and methods. But, there are some restrictions. Enum cannot have a default no-argument constructor and all its constructors (if any) are private; therefore, it is redundant to specify them explicitly.
public enum Deck { JACKS(3), NINES(2), ACES(1), TENS(1), OTHER(0); private final int card; Deck(final int card) { this.card = card; } public int getCardPoint(){ return card; } }
Note that the enum constructor classes are implicit and do not require new keyword. We can write something as follows:
Deck d = Deck.JACKS; System.out.println(d.getCardPoint());
Instance fields come is quite handy in Java; they can be used to give a different meaning altogether to a typical enum instance field.
Enum, Interfaces, and Generics
Enum cannot extend a class, but can implement an interface because Java does not support multiple inheritances of classes but multiple implementation of interfaces. The enum is a default subclass of the generic Enum<T> class, where T represents generic enum type. This is the common base class of all Java language enumeration types. The transformation from enum to a class is done by the Java compiler during compilation. This extension need not be stated explicitly in code. Here is a quick example.
public interface CardPoint { public int getCardPoint(); } public enum Deck implements CardPoint{ JACKS(){ @Override public int getCardPoint(){ return 3; } }, NINES(){ @Override public int getCardPoint(){ return 3; } }, TENS(){ @Override public int getCardPoint(){ return 3; } }, OTHER(){ @Override public int getCardPoint(){ return 3; } }; }
Or, instead of being verbose, we can be little more concise, as follows:
public interfaceCardPoint { public int getCardPoint(); } public enumDeck implements CardPoint{ JACKS(3), NINES(2), ACES(1), TENS(1), OTHER(0); private final int card; Deck(final int card) { this.card = card; } @Override public int getCardPoint(){ return card; } }
The Enum<T> generic base class provides quite a few convenient methods. Two frequently used among them are:
- String name(): Returns the name of the enum constant
- int ordinal(): Returns the ordinal of this enumeration constant
Also, the Java compiler automatically generates two static methods for every enum type.
- T valueOf(String name: Returns enum constant from the name supplied as argument.
- T[] values(): Returns all declared enum constants.
Interface Enumeration<E>
There is an interface in Java API, in the java.til package, called Enumeration<E>. According to the Java API Documentation, “an object that implements the Enumeration interface generates a series of elements, one at a time. Successive calls to the nextElement method return successive elements of the series.” An example given is that, if we want to print all elements of Vector<E> v, we may do so as follows:
for(Enumeration<E> e = v.elements(); e.hasMoreElements();) System.out.println(e.nextElement());
Enum in Collections
There are two enum collection classes: EnumSet and EnumMap.
The EnumSet<T> is a specialized Set implementation to be used with enum types. They are represented internally as bit vectors. The elements in an enum set must have a single enum type and Null elements are not allowed in the set. The EnumSet<T> cannot be instantiated using a constructor; instead, there are many factory methods to do the same.
For example, we can use the allOf method to create an enum set containing all of the elements in the specified element type.
final Set<Deck> cardDeck = EnumSet.allOf(Deck.class);
If we want to creates an empty enum set with the specified element type:
final Set<Deck> noneCardDeck = EnumSet.noneOf(Deck.class);
We also can state specific enum type to include in the set, as follows:
final Set<Deck> specificCardDeck = EnumSet.of( Deck.JACKS, Deck.ACES );
With EnumMap<T,?> we can set key-value pairs with enum elements. It is a specialized Map implementation to be used with enum type keys. All of the keys in an enum map must come from a single enum type that is specified, explicitly or implicitly, when the map is created. Enum maps are represented internally as arrays. Here, the keys are enum constants of the enum type, as follows:
final Map<Deck, String> enumMap = new EnumMap<>(Deck.class); enumMap.put(Deck.JACKS, "J"); enumMap.put(Deck.ACES, "A");
Note that both EnumSet<T> and EnumMap<T,?> are not thread-safe. Therefore, they should be used cautiously in a multithreaded environment.
Points to Remember
Here are few points to remember from the Java Language Specification:
- It is a compile-time error if an enum declaration has the modifier of abstract or final.
- An enum declaration is implicitly final unless it contains at least one enum constant that has a class body.
- A nested enum type is implicitly static. It is permitted for the declaration of a nested enum type to redundantly specify the static modifier.
- It is a compile-time error if the same keyword appears more than once as a modifier for an enum declaration, or if an enum declaration has more than one of the access modifiers public, protected, and private.
- The direct superclass of an enum type E is Enum<E>.
- An enum type has no instances other than those defined by its enum constants. It is a compile-time error to attempt to explicitly instantiate an enum type.
Conclusion
Enum should be used whenever we need to represent a fixed set of constants. Java recommends them because they are designed to be used for that specific purpose. Enum is type-safe, extensible, and fully supported by any modern library or framework.