Enum.valueOf(Class<T> enumType, String name) question

26,170

Solution 1

I think it won't work exactly like this unless you have access to a type variable (through either a type or method signature). The problem is the method signature of Enum.valueOf:

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

There's no way to get a T without a type variable. But you can do it like this if you're willing to suppress some compiler warnings:

public enum King{
    ELVIS
}

@SuppressWarnings({ "unchecked", "rawtypes" })
public static void main(final String[] args){
    final Class<? extends Enum> enumType = King.class;
    final Enum<?> theOneAndOnly = Enum.valueOf(enumType, "ELVIS");
    System.out.println(theOneAndOnly.name());
}

Output:

ELVIS

Solution 2

The problem is with Class<? extends Enum<?>>. We want E extends Enum<E>, but we can't get that because we have two distinct wildcards.

So we need to introduce a generic parameter, possible introduced by calling a method:

enum MyEnum {
    ME;
}

public class EnName {
    public static void main(String[] args) {
        Enum<?> value = of(MyEnum.class, "ME");
        System.err.println(value);
    }
    private static <E extends Enum<E>> E of(Class<E> clazz, String name) {
        E value = Enum.valueOf(clazz, name);
        return value;
    }
}

But reflection is mucky and very rarely what you want. Don't do it.

Solution 3

There's actually an alternate approach: you can use Class.getEnumConstants and roll your own implementation of Enum.valueOf that doesn't have the same type problems. The downside is you get back a generic Enum<?> - but if you knew what type you had coming in, you would be able to use Enum.valueOf anyway.

private static Enum<?> findEnumValue(Class<? extends Enum<?>> enumType, String value) {
    return Arrays.stream(enumType.getEnumConstants())
                 .filter(e -> e.name().equals(value))
                 .findFirst()
                 .orElse(null);
}

(Note that my version returns null if there's no such constant rather than throwing an IllegalArgumentException, but that's a trivial thing to change.

Share:
26,170
Tom
Author by

Tom

Updated on September 08, 2021

Comments

  • Tom
    Tom over 2 years

    I am trying to get around a compile error ("Bound mismatch: ...") relating to dynamic enum lookup.

    Basically I want to achieve something like this:

    String enumName = whatever.getEnumName();
    Class<? extends Enum<?>> enumClass = whatever.getEnumClass();
    Enum<?> enumValue = Enum.valueOf(enumClass, enumName);
    

    Whatever I do, I always end up with that compile error. Honestly, generics and enums are quite mindboggling to me...

    What am I doing wrong here?

  • Brett
    Brett over 13 years
    The question is about enums, generics and reflection. If you ignore generics, what's the point? Particularly going for "rare types" like Class<? extends Enum>.
  • Sean Patrick Floyd
    Sean Patrick Floyd over 13 years
    The point is that a) it can't work otherwise without a helper method or type and b) we know for sure that any Class<? extends Enum> will also satisfy Class<E extends Enum<E>> (because that's the way enum classes work) even though there is no way to check that without a type variable. @SuppressWarnings is an annotation you should only use if you know what you are doing, and I do know.
  • Tom
    Tom over 13 years
    Thanks for this explanation, it helped a lot to further understand whats going on. Since in my particular use case I don't have anything to provide as the generic parameter, I will go for seanizer's answer.