If I, for example, have a method that uses varargs for Class types that extends a super class like this:
public static <E extends Example> void test(Class<E>... es){}
Then I try to call that method with two different subclasses of Example, I can only do so if I make an array with the two classes in it.
//this does not work
test(E1.class,E2.class);
//this does work
test(new Class[]{E1.class,E2.class});
public class E1 extends Example {}
public class E2 extends Example {}
Why is this?
This line doesn't compile:
test(E1.class,E2.class);
There is only one type parameter E
, and Java must match the inferred types of the arguments exactly. It cannot infer Example
, because the objects are Class<E1>
and Class<E2>
, not Class<Example>
. The invariance of Java generics prevents this from happening.
You can work around this by introducing an upper bound wildcard on test
's generic type parameter:
public static <E extends Example> void test(Class<? extends E>... es)
This allows Java to infer Example
for E
, by satisfying the upper bound wildcard with E1
and E2
.
The second line creates a raw array of Class
es, bypassing generics and generating an "unchecked call" warning.
new Class[]{E1.class,E2.class}
If you were to attempt to provide a type argument to Class
here, you would get a compiler error with any half-way reasonable type parameter:
// Needs Class<Example> but found Class<E1> and Class<E2>
test(new Class<Example>[]{E1.class,E2.class});
// Needs Class<E1> but found Class<E2>
test(new Class<E1>[]{E1.class,E2.class});
// Needs Class<E2> but found Class<E1>
test(new Class<E2>[]{E1.class,E2.class});
Satisfying the inference by using a wildcard here just uncovers the real problem here -- generic array creation.
// Generic array creation
test(new Class<? extends Example>[]{E1.class,E2.class});
You define the generic E of a single class that extends Example. You can't reference two different classes in your call because it won't know what type E is. It expects only one type.
Although this does not work:
test(E1.class, E2.class);
This does:
test(E1.class, E1.class);
The reason you can do it with an array is because of type erasure. The compiler doesn't see that the classes within the array are different.
If you change your method to accept any class that extends Example
it will work.
public static void test(Class<? extends Example>...classes)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With