The Trouble With Enums
Java has provided native enum types from version 1.5 onwards. Some issues arise with the use of enums in Java code, which this article attempts to address.
Join the DZone community and get the full member experience.
Join For FreeJava's enums have been around for a while, and it's no surprise that you might run into trouble as new features have been released. This article attempts to cover a few odd cases of enum in action so you don't repeat mistakes of the past.
Enum Abstract Method
An enum type can have abstract methods just like a class. Each enum constant needs to implement the abstract method. An example is as follows:
public enum Animal {
Dog { String sound() { return "bark"; } },
Cat { String sound() { return "meow"; } },
Lion { String sound() { return "roar"; } },
Snake { String sound() { return "hiss"; } };
abstract String sound();
};
Use it as follows:
String str = "Dog";
Animal animal = Animal.valueOf(Animal.class, str);
System.out.println(animal + " makes sound: " + animal.sound());
// prints
Dog makes sound: bark
String to Enum
Use valueOf() to look up an enum by the name.
private enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY
};
Day day = Day.valueOf(Day.class, "MONDAY");
The method throws an IllegalArgumentException if the name (with the exact case) is not found.
Day day = Day.valueOf(Day.class, "Monday");
// throws an IllegalArgumentException
String to Enum Ignore Case
To lookup an enum by string ignoring the case, you can add a static method to the enum class and use it as shown.
static public Day forNameIgnoreCase(String value) {
for (Day day : Day.values()) {
if ( day.name().equalsIgnoreCase(value) ) return day;
}
return null;
}
No exceptions are thrown by this code.
String args[] = { "joe", "monday", "Monday", "MONDAY" };
for (String arg : args) {
Day day = Day.forNameIgnoreCase(arg);
System.out.println(arg + " => " + day);
}
// prints:
joe => null
monday => MONDAY
Monday => MONDAY
MONDAY => MONDAY
EnumSet: Set of Enums
Java also provides a new type EnumSet, which is a Set of Enums and provides a bunch of useful operations.
Select a range of enums using EnumSet.range(). Enum constants are returned in the order of declaration.
for (Animal animal : EnumSet.range(Animal.Cat, Animal.Snake)) {
System.out.println(animal);
}
The EnumSet can also be used as a type-safe alternative to traditional bit flags.
EnumSet.of(Style.BOLD, Style.ITALIC)
You can also add to the set using the normal Set.add() operation. Note that the order of looping is the declared order (and not the add order).
EnumSet<Animal> set = EnumSet.of(Animal.Cat);
set.add(Animal.Dog);
for (Animal animal : set) {
System.out.println(animal);
}
// prints
Dog
Cat
Remove also works in a similar way. In the following example, we are adding all the enum constants to the set using EnumSet.allOf().
EnumSet<Animal> set = EnumSet.allOf(Animal.class);
set.remove(Animal.Snake);
for (Animal animal : set) {
System.out.println(animal);
}
// prints
Dog
Cat
Lion
EnumMap: Enum as a Key
EnumMap is a specialized implementation of Map provided for cases where an enum is used as the key. According to the Javadocs, the storage is compact and efficient. It is used similarly to the way regular Maps are used, with some change in construction; the enum class must be passed into the constructor.
EnumMap<Animal,String> sounds = new EnumMap<Animal,String>(Animal.class);
sounds.put(Animal.Dog, "Bark");
sounds.put(Animal.Cat, "Meow");
for (Map.Entry<Animal,String> e : sounds.entrySet()) {
System.out.println(e.getKey() + " => " + e.getValue());
}
// prints:
Dog => Bark
Cat => Meow
Enum Name Map
The implementation of the values() method creates an array every time it is invoked. To avoid invoking this method too many times, you can create a name map and use it for lookup. (Yes, that might possibly count as “premature optimization” but hopefully you are resorting to this approach only when invoking values() multiple times.)
static private enum Period {
Day, Week, Fortnight, Month, Year;
private static final Map<String,Period> nameMap = new HashMap<>();
static {
for (Period period : Period.values())
nameMap.put(period.name(), period);
};
static public Period forName(String value)
{
return nameMap.get(value);
}
};
And use it like this. Note again that looking up a non-existent name does not result in an IllegalArgumentException.
String[] args = { "joe", "Day", "Week" };
for (String arg : args) {
Period period = Period.forName(arg);
System.out.println(arg + " => " + period);
}
// prints:
joe => null
Day => Day
Week => Week
Comparing Enums: == or equals()?
When comparing enum instances, what should you use?
Day day = ...;
if ( day == Day.MONDAY ) {
// code here
}
if ( day.equals(Day.MONDAY) ) {
// code here
}
Both are correct. In fact, equals() is implemented using ==. Since == never throws a NullPointerException, one might prefer using that.
Should I Use Enum Ordinals?
Enum ordinal is the index of the enum in the list returned by values().
Day[] days = Day.values();
for (int i = 0 ; i < days.length ; i++ ) {
System.out.println(i + " => " + days[i]);
}
// prints:
0 => SUNDAY
1 => MONDAY
2 => TUESDAY
3 => WEDNESDAY
4 => THURSDAY
5 => FRIDAY
6 => SATURDAY
Sometimes you may want to store or transmit the ordinal as a part of storing the state. Should you use the ordinal in such cases? For instance:
System.out.println("4 => " + days[4]); // prints 4 => THURSDAY
The answer is no, it is not a good idea to store or use the ordinal. It is a much better idea to store and transmit the name. Since the values() method returns the array in the order of declaration, using the ordinal might return wrong values if the enum definition is modified to add or remove entries.
Store and use the name. If the enum entry is removed later, valueOf() will throw an exception. Which is much better than using wrong values and not knowing about it.
Summary
We have now learned some basics about enums in Java. Enums in java are more powerful than in most other languages. Abstract methods can be declared for the enum and specialized implementation can be defined for each enum constant. Looking up enum constants in a case-insensitive operation is another area arising frequently.
Published at DZone with permission of Jay Sridhar, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments