Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Varargs of type Class in Java

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?

like image 344
Squirvin Avatar asked Oct 02 '14 21:10

Squirvin


2 Answers

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 Classes, 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});
like image 145
rgettman Avatar answered Oct 14 '22 13:10

rgettman


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)
like image 20
JustinKSU Avatar answered Oct 14 '22 12:10

JustinKSU