17

Here's my issue: given these classes

class A {}
class B extends A {}

This code compiles:

    List<Class<? extends A>> list = Arrays.asList(B.class, A.class);

And this does not:

    List<Class<? extends A>> anotherList = Arrays.asList(B.class);

What gives?


UPDATE: This code compiles in Java 8. Apparently, due to 'Improved Type Inference'.

Victor Sorokin
  • 11,878
  • 2
  • 35
  • 51

6 Answers6

14

In the first example, the inferred type of the Arrays.asList() call is List<Class<? extends A>>, which is obviously assignable to a variable of the same type.

In the second example, the type of the right side is List<Class<B>>. While Class<B> is assignable to Class<? extends A>, List<Class<B>> is not assignable to List<Class<? extends A>>. It would be assignable to List<? extends Class<? extends A>>.

The reason for this is the same one as why a List<B> isn't assignable to List<A>. If it was, it would make the following (not-typesafe) code possible:

List<Class<B>> bb = new ArrayList<B>();
List<Class<? extends A>> aa = bb;
aa.add(A.class);
millimoose
  • 39,073
  • 9
  • 82
  • 134
  • Understood. Could you point me to JLS/Sun tutorial where can I read how inference work? – Victor Sorokin Nov 11 '11 at 12:28
  • Java Generics tutorial http://download.oracle.com/javase/tutorial/java/generics/gentypeinference.html – bpgergo Nov 11 '11 at 12:34
  • 1
    @Victor The relevant bit of the JLS seems to be this: http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#341287 – millimoose Nov 11 '11 at 12:35
  • The tutorials mostly mention that type inference does occur without going into detail. My intuitive understanding is that type inference tries to find the most specific type parameters that will match the compile-time types of all the method. – millimoose Nov 11 '11 at 12:37
5

This will compile:

List<Class<? extends A>> numbers = Arrays.<Class<? extends A>>asList(B.class);
H-Man2
  • 3,169
  • 20
  • 19
  • 1
    +1 this is the correct solution. explicitly specifying the parameter because it differs from what is inferred – newacct Nov 11 '11 at 20:26
2
Arrays.asList(B.class);

is generified as

List<Class<B>> numbers = Arrays.asList(B.class);

Since it has only 1 attribute of type T that match the parameterized type (in this case, B.class).

Buhake Sindi
  • 87,898
  • 29
  • 167
  • 228
1

Damn good question, and I don't know the answer, but here's a work around:

List<Class<? extends A>> numbers = new ArrayList<Class<? extends A>>(Arrays.asList(B.class));
Bohemian
  • 412,405
  • 93
  • 575
  • 722
1

Just to add, we can rewrite the code as below

List<Class<? extends A>> asListA = Arrays.asList(B.class, A.class);
List<Class<B>> asListB = Arrays.asList(B.class);

List<Class<? extends A>> numbers = asListA;
List<Class<? extends A>> numbers2 = asListB; // it will fail here

And @Inerdia explained the details already.

Kowser
  • 8,123
  • 7
  • 40
  • 63
0

This is a problem of Covariance/Contravariance in Generics. The following SO question should help you understand and solve the problem:

Demonstrate covariance and contravariance in Java?

How would contravariance be used in Java generics?

Community
  • 1
  • 1
S2S2
  • 8,322
  • 5
  • 37
  • 65