Compilation error for several common Java parameters

So strange! First look at the code:

public class A {} public class B extends A {} public class C extends A {} public class TestMain { public <T extends A> void test(T a, T b) {} public <T extends A> void test(List<T> a, List<T> b) {} public void test1(List<? extends A> a, List<? extends A> b) {} public static void main(String[] args) { new TestMain().test(new B(), new C()); new TestMain().test(new ArrayList<C>(), new ArrayList<C>()); new TestMain().test(new ArrayList<B>(), new ArrayList<C>()); new TestMain().test1(new ArrayList<B>(), new ArrayList<C>()); } } 

In the statement new TestMain().test(new ArrayList<B>(), new ArrayList<C>()) a compilation error appears:

Associated mismatch: a general test of a method (T, T) of type TestMain is not applicable for arguments (ArrayList<B>, ArrayList<C>) . The inferred type ArrayList<? extends A> ArrayList<? extends A> not a valid replacement for the <T extends A> limited parameter

But:

  new TestMain().test(new B(), new C()) --> compiled ok new TestMain().test(new ArrayList<C>(), new ArrayList<C>()) --> compiled ok new TestMain().test1(new ArrayList<B>(), new ArrayList<C>()) --> compiled ok 

If we define a generic type before the method name, it seems that the type of the second generic List parameter should be the same as the first. But there are no restrictions if we define general parameters.

Is this a sign or a compilation error? Is there any documentation about this?

+7
java collections generics compiler-errors
source share
2 answers

Absolutely no mistake; you just misunderstand the rules of subtyping in generics.

Since we have B extends A :

  • B is a subtype of A
  • a instanceof B also instanceof A

Since Java arrays are covariant:

  • B[] is a subtype of A[]
  • a instanceof B[] also instanceof A[]

However, Java generics are invariant:

  • List<B> NOT a subtype of List<A>
  • a instanceof List<B> NOT an instanceof List<A> .

If you have the following generic method declaration:

 public <T extends A> void test(List<T> a, List<T> b) 

Then, as explicitly stated here, A and B must be of the same type, List<T> , for some transformation of capturing a parameter of type <T extends A> .

Since List<B> and List<C> are two different types, you cannot mix them as actual arguments for test . In addition, although B and C are subtypes of A , generics are invariant, so neither List<B> nor List<C> is List<A> .

Thus,

 test(new ArrayList<B>(), new ArrayList<C>()); // error!!! doesn't compile!!! 

does not compile, which is the expected behavior.

see also

Related Questions

In generics typing rules:

  • Any easy way to explain why I can't do List<Animal> animals = new ArrayList<Dog>() ?
  • java generics (non) covariance
  • What is a raw type and why shouldn't we use it?
    • Explains that the type of raw List is different from List<Object> , which is different from List<?>

When using super and extends :

  • Java Generics: What is PECS?
    • From efficient Java 2nd Edition: "manufacturer extends consumer super "
  • What is the difference between super and extends in Java Generics .
  • What is the difference between <E extends Number> and <Number> ?
  • How to add List<? extends Number> data structures List<? extends Number> List<? extends Number> ? (YOU CAN NOT!)

About current common errors:

  • Generics compiles and runs in Eclipse but does not compile in javac
+17
source share

This is not a mistake, just these generics are complicated. Try changing the second test method to:

  public <T extends A, K extends A> void test(List<T> a, List<K> b) { 

Basically, in what you have, there is no type T that can satisfy what you go through, unlike the first method, where B and C are treated only as A. The actual name of this behavior eludes me, but should in the literature there are many examples.

In short, although B is a child of A, List <B> is not a child of List <A>.

+9
source share

All Articles