Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 625
Tom Avatar asked Oct 25 '10 11:10

Tom


People also ask

What is enum valueOf?

valueOf() method returns the enum constant of the specified enumtype with the specified name. The name must match exactly an identifier used to declare an enum constant in this type.

What is valueOf enum return?

valueOf(Class<T> enumType, String name) Returns the enum constant of the specified enum type with the specified name.

What is enumeration explain values () and valueOf () methods?

Values() and valuesOf() methods The values() method returns an array that contains a list of the enumeration constants and the valueOf() method returns the enumeration constant whose values corresponds to the string passed in str.

Can enum contains string?

No they cannot. They are limited to numeric values of the underlying enum type.


3 Answers

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

like image 66
Sean Patrick Floyd Avatar answered Oct 11 '22 14:10

Sean Patrick Floyd


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.

like image 7
Sbodd Avatar answered Oct 11 '22 14:10

Sbodd


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.

like image 27
Tom Hawtin - tackline Avatar answered Oct 11 '22 13:10

Tom Hawtin - tackline