Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a difference between the generic bounds "Enum<T> & Foo" and "Enum<? extends Foo>"

Tags:

java

generics

Are these two (valid) generic bounds:

<T extends Enum<T> & MyInterface>
<T extends Enum<? extends MyInterface>>

the same?


Suppose I have an interface

interface MyInterface {
    void someMethod();
}

And some enums that implement it:

enum MyEnumA implements MyInterface {
    A, B, C;
    public void someMethod() {}
}

enum MyEnumB implements MyInterface {
    X, Y, Z;
    public void someMethod() {}
}

And I want to require that an implementation uses not only a MyInterface but also that it is an enum. The "standard" way is by an intersection bound:

static class MyIntersectionClass<T extends Enum<T> & MyInterface> {
    void use(T t) {}
}

But I've discovered that this also works:

static class MyWildcardClass<T extends Enum<? extends MyInterface>> {
    void use(T t) {}
}

With the above, this compiles:

public static void main(String[] args) throws Exception {
    MyIntersectionClass<MyEnumA> a = new MyIntersectionClass<MyEnumA>();
    a.use(MyEnumA.A);
    MyWildcardClass<MyEnumB> b = new MyWildcardClass<MyEnumB>();
    b.use(MyEnumB.X);
}

And the bound works as and intended and required by above for both cases.

Is there a difference between these two bounds, if so what, and is one "better" than the other?

like image 878
Bohemian Avatar asked Jan 02 '13 13:01

Bohemian


2 Answers

In this specific case there is no difference because Enums formal type parameter is effectively the self type. This is because one can not inherit from Enum like so:

class MyEnumA extends Enum<MyEnum2> {}
class MyEnumB implements MyInterface {}

So yes, semantically they're the same bound, but only because it's Enum.

like image 71
Ben Schulz Avatar answered Oct 27 '22 09:10

Ben Schulz


As others have pointed out, both syntaxes achieve the same bounds - and only because of the special case of enums, where we know the T in Enum<T> must be the immediately extending enum type. So in restricting what T can be resolved to, there's no difference.

There is a difference in the possible usage of instances of T, but it's probably such a nuance that it's irrelevant. Consider that the following statement compiles in MyIntersectionClass.use but not MyWildcardClass.use:

T t2 = t.getDeclaringClass().newInstance();

Only these will compile in the latter:

MyInterface t2 = t.getDeclaringClass().newInstance();
Enum<? extends MyInterface> t3 = t.getDeclaringClass().newInstance();
like image 33
Paul Bellora Avatar answered Oct 27 '22 09:10

Paul Bellora