Case-insensitive matching of a string to a Java enum

Java provides a valueOf() method for every Enum<T> object, so given an enum like

public enum Day {
  Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
}

one can do a lookup like

Day day = Day.valueOf("Monday");

If the string passed to valueOf() does not match (case sensitive) an existing Day value, an IllegalArgumentException is thrown.

To do a case-insensitive matching, one can write a custom method inside the Day enum, e.g.

public static Day lookup(String day) {
  for (Day d : Day.values()) {
    if (d.name().equalsIgnoreCase(day)) {
      return type;
    }
  }
  return null;
}

Is there any generic way, without using caching of values or any other extra objects, to write a static lookup() method like the above only once (i.e., not for every enum), given that the values() method is implicitly added to the Enum<E> class at compile time?

The signature of such a "generic" lookup() method would be similar to the Enum.valueOf() method, i.e.:

public static <T extends Enum<T>> T lookup(Class<T> enumType, String name);

and it would implement exactly the functionality of the Day.lookup() method for any enum, without the need to re-write the same method for each enum.


Solution 1:

I found getting the special blend of generics a little tricky, but this works.

public static <T extends Enum<?>> T searchEnum(Class<T> enumeration,
        String search) {
    for (T each : enumeration.getEnumConstants()) {
        if (each.name().compareToIgnoreCase(search) == 0) {
            return each;
        }
    }
    return null;
}

Example

public enum Horse {
    THREE_LEG_JOE, GLUE_FACTORY
};

public static void main(String[] args) {
    System.out.println(searchEnum(Horse.class, "Three_Leg_Joe"));
    System.out.println(searchEnum(Day.class, "ThUrSdAy"));
}

Solution 2:

I would think the easiest safe way to do it would be:

Arrays.stream(Day.values())
    .filter(e -> e.name().equalsIgnoreCase(dayName)).findAny().orElse(null);

Or if you want to use the class object, then:

Arrays.stream(enumClass.getEnumConstants())
    .filter(e -> (Enum)e.name().equalsIgnoreCase(dayName)).findAny().orElse(null);

Solution 3:

Starting from version 3.8 apache commons-lang EnumUtils has two handy methods for this:

  • getEnumIgnoreCase(final Class<E> enumClass, final String enumName)
  • isValidEnumIgnoreCase(final Class<E> enumClass, final String enumName)

Solution 4:

A generic solution would be to keeo to the convention that constants are uppercase. (Or in your specific case use a capitalize on the look-up string).

public static <E extends Enum<E>> E lookup(Class<E> enumClass,
        String value) {
    String canonicalValue.toUpperCase().replace(' ', '_');
    return Enum<E>.valueOf(enumClass, canonicalValue);
}

enum Day(MONDAY, ...);
Day d = lookup(Day,class, "thursday");