6

The following code compiles (and runs tests as expected) in Eclipse:

import java.util.EnumSet;
public class EnumTest {

    static enum Cloneables implements Cloneable {
        One, Two, Three;
    }

    public <T extends Cloneable> T getOne(Class enumType) {
        EnumSet<? extends T> set = EnumSet.allOf(enumType);
        return set.iterator().next();
    }
}

However, compiling with either javac (JDK 7) directly or via Maven fails with the following error:

type argument ? extends T is not within bounds of type-variable E

To be honest, the complexity of enums + interfaces + type-parameters (generics) all at play at once threw me off as I was writing the code, but I thought I had finally gotten it right.

The goal is to write calling code like this:

Cloneable something = enumTest.getOne(Cloneables.class);

For example, in Eclipse the following test compiles and passes:

@Test
public void testGetFirst() {
    assertSame(Cloneables.One, getOne(Cloneables.class));
}

Any clues about which is "correct," Eclipse or javac, are appreciated.

Also appreciated is any advice about alternate ways to implement the idea: take a class as a method param that can be used in EnumSet.allOf() and that also determines the type of Enum objects in the EnumSet

By the way, don't bother critiquing the purpose of this method; I've reduced it down from more useful/meaningful code. I'm not interested in discussing the merits of "finding the first element from an enum type" - that's not the point of this question.

E-Riz
  • 31,431
  • 9
  • 97
  • 134
  • 1
    It appears that this may be a bug in javac (JDK 1.7.0_60). The accepted answer below is a work-around (and actually cleaner code). See the detailed analysis at https://bugs.eclipse.org/bugs/show_bug.cgi?id=456459#c7 – E-Riz Jan 05 '15 at 14:06

1 Answers1

3

You need to make sure that T is an enum type, or it won't meet the constraints for EnumSet:

public <T extends Enum<T> & Cloneable> T getOne(Class enumType) 

Also, you don't need the wildcard in your EnumSet, and you shouldn't use the raw Class type:

public <T extends Enum<T> & Cloneable> T getOne(Class<T> enumType) {
    EnumSet<T> set = EnumSet.allOf(enumType);
    return set.iterator().next();
}
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • 1
    I never even knew you could apply the `&` operator when declaring a type parameter like that. Even after almost 18 years writing Java code, I learned something new today! – E-Riz Dec 31 '14 at 22:58
  • By the way, the reason I used the raw form of `Class` is that without `T` extending `Enum` the Eclipse compiler wouldn't accept any value for the `Class` type parameter. So using it raw was a side-effect of not knowing to combine `Cloneable` and `Enum` for `T`. – E-Riz Dec 31 '14 at 23:00
  • Interestingly, Eclipse won't let me declare the set as `EnumSet` - it complains that *"Bound mismatch: The type T is not a valid substitute for the bounded parameter > of the type EnumSet"*. Luckily, `EnumSet extends T?` works and is equivalent. – E-Riz Dec 31 '14 at 23:03
  • 1
    @E-Riz: No; actually, the bound should be `T extends Enum & Cloneable`. You can then remove the wildcard again. – SLaks Dec 31 '14 at 23:06
  • @E-Riz Given that generics were added to Java in 2004, you've only had 10 years (almost 11) to learn about them :-). I find generics to be the most complex area of Java by far. Using them is not so bad, but when it comes to declaring any generic methods/classes, it can be quite formidable. – Joseph K. Strauss Jan 01 '15 at 03:45