4

I want to pass a list of enum classes to a method, where all of the enums implement a common interface, and have the method return one of the enum values.

Looking at Java Generics Wildcarding With Multiple Classes, it seems that

public class Main
{
    interface Foo {}

    enum First implements Foo {
        A, B, C;
    }

    enum Second implements Foo {
        X, Y, Z;
    }

    interface Bar {}

    enum Third implements Bar {
        M, N, P;
    }
    enum Fourth implements Bar {
        A, X, Z;
    }


    public static <I, T extends Enum<?> & I> 
      I enumVarArgs(Class<? extends T>... classes) 
    {
        // Do stuff and return some instance of T
        return null;
    }

    public static void main(String[] args) {
        Foo foo = enumVarArgs(First.class,
                              Second.class);
        Bar bar = enumVarArgs(Third.class,
                              Fourth.class);
    }
}

should do what I want. However, this fails to compile under Java 10:

[ERROR] /me/test/src/main/java/test/Main.java:[17,42] error: unexpected type
  required: class
  found:    type parameter I
  where I,T are type-variables:
    I extends Object declared in method <I,T>enumVarArgs(Class<? extends T>...)
    T extends Enum<?>,I declared in method <I,T>enumVarArgs(Class<? extends T>...)
[INFO] 1 error

From the error message, I am guessing that Java wants me to do something like T extends Enum<?> & Serializable, where I pass an actual interface, rather than a type parameter. However, I need the API to be general so that I remains a generic parameter.

Is there a syntax that makes this work?

If it matters, we are using Java 10.

Troy Daniels
  • 3,270
  • 2
  • 25
  • 57

2 Answers2

0

If interfaces are your design, then you can make the two interfaces extend a marker interface:

interface FooBar{}

interface Foo extends FooBar {}

interface Bar extends FooBar {}

And you can use this marker interface:

public static <T extends Enum<?> & FooBar> 
      FooBar enumVarArgs(Class<? extends T>... classes) {
    return null;
}
ernest_k
  • 44,416
  • 5
  • 53
  • 99
  • 1
    Declaring `I extends FooBar` and returns an `I` means you need an unchecked cast from `FooBar` to `I` in this method – yyyy Mar 08 '19 at 18:14
  • @yyyy That's absolutely right. I didn't catch that surely because I didn't have to implement the method. Thanks, I've removed the second solution. – ernest_k Mar 08 '19 at 18:22
0

I think what you need is:

interface OnlyImpelents<T> {}
interface Foo extends OnlyImpelents<Foo> {}
interface Bar extends OnlyImpelents<Bar> {}

static <T extends Enum<?> & OnlyImpelents<? super T>> T enumVarArgs(T... values) {
    return values[0];
}
public static void main(String[] args) {
    Foo foo = enumVarArgs(First.A, Second.X);
}

The marker interface OnlyImpelents<T> prevents any class from implementing both Foo and Bar

The only way to get enum values from a enum class is via reflection, which doesn't need to have a static type. Personally I think this is not a good object-oriented design, using T... as args should be better

yyyy
  • 593
  • 2
  • 10