15

I have a method that needs to accept a Enum class. These enums implement an interface. Now I need access to both Enum methods like ordinal(), name(), etc and my interface methods. What I've tried:

public <T extends ConfigFeature, Enum> void showEnabledFeatures(Class<T> enumType, long mask) {
    List<T> list = Arrays.asList(enumType.getEnumConstants());
    list.forEach(item -> {
        // My interface's method, works fine
        item.getMask();
        // Enum method doesn't work:
        // item.ordinal();
    });
}

Reversing the order reverses the working:

public <T extends Enum, ConfigFeature> void showEnabledFeatures(Class<T> enumType, long mask) {
    List<T> list = Arrays.asList(enumType.getEnumConstants());
    list.forEach(item -> {
        // My interface's method, doesn't work now
        // item.getMask();
        // Enum method now works:
        item.ordinal();
    });
}

Is there a way to get access to both methods from interface and Enum?

ayushgp
  • 4,891
  • 8
  • 40
  • 75
  • 1
    Possible duplicate of [Java Generics Wildcarding With Multiple Classes](https://stackoverflow.com/questions/745756/java-generics-wildcarding-with-multiple-classes) – Julien Lopez Mar 09 '18 at 10:49

4 Answers4

23

You are using the wrong syntax to say that T must implement this interface AND is an enum.

This:

<T extends ConfigFeature, Enum>

is not constraining T to Enum, but actually creating a new generic parameter called Enum.

Similarly,

<T extends Enum, ConfigFeature>

is not constraining T to ConfigFeature. You are declaring a new generic parameter called ConfigFeature.

The correct syntax is to use &:

<T extends Enum<T> & ConfigFeature>

Note that the order is actually important here! Enum can only come first.

According to here, only the first constraint can be a class, and then the ones after it must all be interfaces:

TypeParameter:
    {TypeParameterModifier} Identifier [TypeBound]

TypeParameterModifier:
    Annotation

TypeBound:
    extends TypeVariable
    extends ClassOrInterfaceType {AdditionalBound}

AdditionalBound:
    & InterfaceType
Salem
  • 13,516
  • 4
  • 51
  • 70
Sweeper
  • 213,210
  • 22
  • 193
  • 313
14

Your syntax is wrong; you need:

public <T extends Enum<T> & ConfigFeature>

That syntax that you used creates two generic type parameters one called T and one called Enum (where Enum isn't bounded and T is bounded to extend ConfigFeature).

Note that, to avoid any generics warnings about the use of raw types, you also have to provide a type parameter to the Enum bound. An enum called X always extends Enum<X>, so you can use T extends Enum<T>, and the full text of the method local generic declaration becomes <T extends Enum<T> & ConfigFeature>

Erwin Bolwidt
  • 30,799
  • 15
  • 56
  • 79
6

Replace the , in your second example with &. You can use & to declare multiple bounds as long as they’re interfaces from the second type onwards. If you use a comma it’s a separate type parameter, not a bound.

cpp beginner
  • 512
  • 6
  • 23
1

Just to add to existing answers, instead of using Mutliple Bounds as described in other answers, you can define interface that combines interface you want with return method for enum, like:

public interface ConfigFeatureEnumI <T extends Enum<T>> extends ConfigFeatureI{
    @SuppressWarnings("unchecked")
    default public T asEnum() {
        return (T) this;
    }
}

You can implement asEnum() in enum used, or just use default method if Java 8 is available as I show here.

public enum ConfigEnum implements ConfigFeatureEnumI<ConfigEnum>{//...

Then showEnabledFeatures can look like:

public <T extends ConfigFeatureEnumI<?>> void showEnabledFeatures(Class<T> enumType, long mask) {
    List<T> list = Arrays.asList(enumType.getEnumConstants());
    list.forEach(item -> {
        // Interface method works:
        item.getMask();
        // Enum method works via asEnum():
        item.asEnum().ordinal();
    });
}

While adding new Interfaces is not ideal, it can be easier to use for programmers who do not know Java generics that well or never used Multiple bounds (I use generics a lot, but never needed such feature , so I was a bit off when I did see it).

Piro
  • 1,367
  • 2
  • 19
  • 40
  • But this would be casting on every call to any function from the enum, which IMO is unnecessary. Multiple Bounds provides a much cleaner (and better performing?) solution. – ayushgp Mar 13 '18 at 06:09
  • 1
    @ayushgp it is probably cleaner solution (if you know generics well), but as your question shows it is easy to miss things out. My point is it can be done without it and before seeing your question I would never think about using Multiple bounds – Piro Mar 13 '18 at 06:31