Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using interface methods of enum in method accepting class

Tags:

java

enums

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?

like image 307
ayushgp Avatar asked Mar 09 '18 07:03

ayushgp


4 Answers

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
like image 162
Sweeper Avatar answered Oct 20 '22 03:10

Sweeper


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>

like image 14
Erwin Bolwidt Avatar answered Oct 20 '22 05:10

Erwin Bolwidt


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.

like image 6
cpp beginner Avatar answered Oct 20 '22 03:10

cpp beginner


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).

like image 1
Piro Avatar answered Oct 20 '22 03:10

Piro