19

I'm trying to do the following:

Class<?> cls = unknownClass;
if(cls.isEnum()){
    @SuppressWarnings("unchecked")
    Class<? extends Enum<?>> enumClass = (Class<? extends Enum<?>>) cls;
    Object val = Enum.valueOf(enumClass, "NAME1");
}

But I get the following error:

Bound mismatch: The generic method valueOf(Class<T>, String) of type Enum<E> is 
not applicable for the arguments (Class<capture#5-of ? extends Enum<?>>, String). 
The inferred type capture#5-of ? extends Enum<?> is not a valid substitute for 
the bounded parameter <T extends Enum<T>>   

Can someone tell me what I am doing wrong?

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
cammil
  • 9,499
  • 15
  • 55
  • 89

5 Answers5

20

Given that the cast won't really be checking things, I'd go with the completely raw version:

if (cls.isEnum()){
    @SuppressWarnings("unchecked")
    Object val = Enum.valueOf(cls, "NAME1");
}

That seems to work. Complete example:

public class Test
{
    enum Foo
    {
        BAR, BAZ
    }


    public static void main(String[] args)
    {
        @SuppressWarnings("rawtypes")
        Class cls = Foo.class;

        if (cls.isEnum())
        {        
            @SuppressWarnings("unchecked")
            Object value = Enum.valueOf(cls, "BAR");
            System.out.println(value);
        }
    }
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • @dacwe: Well it works on *my* compiler - but you haven't said in what *way* it doesn't work. I'll show a short but complete example... – Jon Skeet Mar 10 '11 at 16:19
  • Ah, sorry, it does not compile. `Class> cls = SomeEnum.class; Object val = Enum.valueOf(cls, "NAME1");` and I get a simular `Bound mismatch` as the asker. – dacwe Mar 10 '11 at 16:21
  • 1
    @dacwe: You hadn't shown us the declaration of `cls`. Just change it to the raw type `Class` instead of `Class>`. See my edit for a complete example. – Jon Skeet Mar 10 '11 at 16:22
  • (And if you can't change the declaration for `cls`, use `@SuppressWarnings("rawtypes") Class clazz = cls;` and then use `clazz` instead. – Jon Skeet Mar 10 '11 at 16:24
7

The problem is that you have two ? and they could be different and they have to be the same.

You either have to use a non generic of declare a generic type like

public static <T extends Enum<T>> T val(Class<T> cls) {
    if(cls.isEnum()) { // is redundant when the compiler checks this.
        @SuppressWarnings("unchecked")
        Class<T> enumClass = (Class<T>) cls;
        T val = Enum.valueOf(enumClass, "NAME1");
        return val;
    }
    return null;
}

However, calling this method is a bit of a nightmare as well. ;)

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
5

Class.getEnumConstants will give all the enums, which you can then find the one you are interested in.

Modifying Jon's code:

public class Test {
    private enum Foo {
        BAR, BAZ
    } 

    public static void main(String[] args) {
        Class<?> cls = Foo.class;

        if (cls.isEnum()) { 
            for (Object constant : cls.getEnumConstants()) { 
                Enum<?> enumConstant = (Enum<?>)constant;
                if (enumConstant.name().equals("BAR")) {
                    System.out.println(constant);
                    break;
                }
            }
        }
    }
}

In Java SE 8, you'll possibly be able do something clever with a lambda and abstracted control flow.

Note:

  • Reflection is almost always a really bad idea.
  • No need for unchecked warning or suppression of those valid warnings.
  • No need for rawtypes (which ever spelling and semantics) warning or suppression of those valid warnings.
  • No need for an unfounded rant.
  • No need to downvote this answer.
Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
  • I like that if I don't know the enum type at compile time, this solution won't throw a compiler warning. I can have just an instance of type foo and call fooInstance.getClass() and use that for cls. – Cameron Mar 07 '13 at 23:09
1

Using raw type is good. While Java was reluctant to add raw type, it has been proven to be necessary and indispensable, beyond backward compatibility issues.

Without raw types, to solve your problem in a theoretically perfect way, Java has to be way more complicated. It probably needs a type-variable-variable. At that point, the code is no longer to please the humans, it is to please the machines. (We are probably already at this point, considering all the generics code that are impossible to understand)

irreputable
  • 44,725
  • 9
  • 65
  • 93
-1
    class EnumValues<P extends Enum<P>>
    {
        public static <P extends Enum<P>> P [] getValues( Class<P> keyType)
        {
            return keyType.getEnumConstants();
        }
    }

Usage:

    Class cls = Thread.State.class;
    for( Enum en : EnumValues.getValues( cls)) 
        System.out.println( en.name()); 
romsky
  • 864
  • 1
  • 8
  • 11
  • Calling a generic method with raw types is a bit on the naughty side. Effectively there's an unchecked cast there. Recent compilers will give you warnings (heed them!). (Not my downvote, btw.) – Tom Hawtin - tackline Feb 27 '12 at 02:49