8

I have code which is setting values on an object using its setter methods. One of the setters takes an Enum type as the method parameter. The code looks something like this:

    String value = "EnumValue1";
    Method setter = getBeanWriteMethod("setMyEnumValue");
    Class<?> type = setter.getParameterTypes()[0];
    Object convertedValue = null;
    if (type.isEnum()) {
       convertedValue = convertToEnum(value, type);
    } else {
       convertedValue = ClassUtils.convertType(value, type);
    }
    return convertedValue;

The question is what to put in the convertToEnum method. I know I could "brute force it" by iterating the enum constants (or the fields) of the type object, matching the value. Am I overlooking a simpler way to do it using Reflection? (I looked at several examples, but didn't find any where the enum was only know via Class).

3 Answers 3

23

Off the top of my head:

  Enum<?> convertedValue = Enum.valueOf((Class<Enum>)type,  value);

This will convert a string to an enum constant of the Enum class of type

Edit: Now that I have a computer handy, I can see what actually works. Either of the below cases ran correctly without compiler warnings:

Enum<?> convertedValueA = Enum.valueOf(type, value);
Enum<?> convertedValueB = Enum.valueOf(type.asSubclass(Enum.class), value);

The second one calls asSubClass() which would do a runtime check to ensure that type is some enum class, but the valueOf() method has to make that check anyway in order to work correctly.

The following gave me a compile error:

Enum<?> convertedValueC = Enum.valueOf((Class<? extends Enum <?>>)type, value);

java: Foo.java:22: <T>valueOf(java.lang.Class<T>,java.lang.String) in java.lang.Enum cannot be applied to (java.lang.Class<capture#134 of ? extends java.lang.Enum<?>>,java.lang.String)

The intricacies of casting to wildcard types confuses me so I've probably tried the wrong cast. Plus the fact that it has no runtime effect means that it's easy to get it wrong and never find out.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks, I must have gotten the cast wrong when I first tried that. This is correct solution. Unfortunately, after trying this, I found that I need to do a case-insensitive match, which Enum.valueOf() won't do. So back to the brute force approach...
That case is awful. type isn't Enum.class. In any case, asSubclass is better than a hack cast.
@TomHawtin-tackline: using as asSubclass gives me compiler warnings. Class<? extends Enum> enumClass = type.asSubclass(Enum.class); Enum<?> convertedValue = Enum.valueOf(enumClass, "Value1"); for both Raw Type, and also Unvhecked Invocation. So I'm not sure I see how that is better.
The issue is that if the type variable is declared as Class<?>, then it has to be cast or it won't compile. If it is declared as Class (no wildcard), then it works without casting. I got messed up because the return type of Method.getParameterTypes() is Class<?>[], which set me up to have the messiness of needing the cast.
Enum.valueOf(type, value); gives me "Bound mismatch: The generic method valueOf(Class<T>, String) of type Enum<E> is not applicable for the arguments (Class<capture#2-of ?>, String). The inferred type capture#2-of ? is not a valid substitute for the bounded parameter <T extends Enum<T>>". The other one gives me a warning: "Type safety: Unchecked invocation valueOf(Class<capture#3-of ? extends Enum>, String) of the generic method valueOf(Class<T>, String) of type Enum" – because the "asSubClass" returns a Class<? extends Enum> instead of Class<? extends Enum<E>>.
4

You can use

Class enumType = ....
String name = ....

Enum e = Enum.valueOf(enumType, name);

e.g.

import java.lang.annotation.*;

public class Main {
    public static void main(String[] args) {
        Class enumType = RetentionPolicy.class;
        String name = "SOURCE";

        Enum e = Enum.valueOf(enumType, name);
        System.out.println(e);
    }
}

prints

SOURCE

5 Comments

@SamGoldberg That's a raw type. So I think it will work. Just badly.
@TomHawtin-tackline: I can't get it to compile without the cast.
@SamGoldberg See my example. No cast is required.
Yes, I see. If I change Class<?> to Class, then it works and compiles fine.
@SamGoldberg and the code is simpler, but does the same thing.
1
Enum.valueOf(yrEnum, myString)

Returns the enum where myString is the name of the enum instance you want. And yrEnum.values() returns an array of all the different possible Enums instances.

1 Comment

As with previous, the cast to Class<Enum> is required to make this work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.