Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

The Trouble With Enums

DZone's Guide to

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.

· Java Zone
Free Resource

Make it happen: rapid app development on Kubernetes as a managed service.

Java'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.

Tutorial: WordPress as distributed microservices on Kubernetes.

Topics:
java enums ,java ,abstract methods ,type safe ,tutorial

Published at DZone with permission of Jay Sridhar, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}