Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java generics compiler error: incompatible types

Tags:

java

generics

When doing some not really fancy things with Java, I came over an error with generics that I was not able to understand why it doesn't work. The code is:

package test;
import java.util.*;
public class TestClass {
  public static class A extends C{}
  public static class B extends C{}
  public static class C{}
  public static class D<T>{}
  public static class E<T>{}

  public static void main(String args[]){
      E<D<? extends C>> a = new E<D<A>>();
      E<D<? extends Object>> b = new E<D<? extends C>>();
      E<D<? extends A>> c = new E<D<A>>();
      E<D<? super A>> d = new E<D<A>>();
      D<? extends C> e = new D<A>();
      D<? extends A> f = new D<A>();
      D<? extends A> g = new D<A>();
  }
}

The error I get when compiling is:

test/TestClass.java:11: incompatible types
found   : test.TestClass.E<test.TestClass.D<test.TestClass.A>>
required: test.TestClass.E<test.TestClass.D<? extends test.TestClass.C>>
      E<D<? extends C>> a = new E<D<A>>();
                            ^
test/TestClass.java:12: incompatible types
found   : test.TestClass.E<test.TestClass.D<? extends test.TestClass.C>>
required: test.TestClass.E<test.TestClass.D<? extends java.lang.Object>>
      E<D<? extends Object>> b = new E<D<? extends C>>();
                                 ^
test/TestClass.java:13: incompatible types
found   : test.TestClass.E<test.TestClass.D<test.TestClass.A>>
required: test.TestClass.E<test.TestClass.D<? extends test.TestClass.A>>
      E<D<? extends A>> c = new E<D<A>>();
                            ^
test/TestClass.java:14: incompatible types
found   : test.TestClass.E<test.TestClass.D<test.TestClass.A>>
required: test.TestClass.E<test.TestClass.D<? super test.TestClass.A>>
      E<D<? super A>> d = new E<D<A>>();
                          ^
4 errors

If E<D<? extends C>> is found, that should surely match E<D<? extends Object>>, right? Or have I missed something?

like image 993
arnebef Avatar asked Aug 14 '09 09:08

arnebef


3 Answers

Maybe this would help you understand:

    ArrayList<Object> aList = new ArrayList<String>();

This doesn't compile either with a similar error.

EDIT: Consult with: http://www.ibm.com/developerworks/java/library/j-jtp01255.html

like image 114
Grzegorz Oledzki Avatar answered Oct 21 '22 14:10

Grzegorz Oledzki


This is basically the same case as posted previous. Basically, in a generics case, you are never allowed to do this assigment:

Think of this example:

ArrayList<Object> alist = new ArrayList<Number>();

This doesn't compile because it is not type safe. You could possibly add Strings aList. You are trying to assign a list of objects that are guaranteed to be Numbers but can be any Number, to a list that only guarantees you to contain objects but that can be any objects. If the compiler allowed this case it would loosen the restriction on which types of objects are allowed to get into the list. This is why you must use the wildcard ?, as such:

ArrayList<? extends Object> alist = new ArrayList<Number>();

To the compiler ArrayList<? extends Object>, means "an ArrayList of some specific type '?' that I don't know, but which I know extends Object. This ArrayList is guaranteed to contain only elements of this unknown '?' type, and therefore contains only objects". In this case the compiler will however not allow you to do alist.add(2). Why is that the case, because the compiler doesn't know the type of the elements of the list, and can't guarantee that you are allowed to insert Integer objects into it.

You are right in thinking that D<? extends Object> is a supertype of D<? extends C>. However, List<D<? extends Object>> is not a subtype of List<D<? extends C>>, you should be using List<? extends D<? extends C>>.

Your case is basically equivalent to

ArrayList<D<? extends Object>> alist = new ArrayList<D<? extends C>>();

You have the same problem as above, the list on the right hand side can only contain object of class D whose type Parameter is C, and you are trying to assign it to a list (on the left hand side) can contain objects of class D whose type parameter can be any object.

So if the compiler allowed your code would not be type safe, and the following would fail.

ArrayList<D<? extends Object>> alist = new ArrayList<D<? extends C>>(); //< not type safe
alist.add(new D<Number>); //< oops

In short, what you need for your specific example is the following:

// type parameter of left hand side is ? extends subtype
List<? extends D<? extends Object>> b = Arrays.asList(new D<A>(), new D<B>()); 

// type parameter of left hand side is identical
List<D<? extends C>> b = Arrays.asList(new D<A>(), new D<B>());

// type parameter of left hand side is ? extends subtype
List<? extends D<? extends C>> c = Arrays.asList(new D<A>());

// type parameter of left hand side is identical
List<D<A>> c = Arrays.asList(new D<A>());

Hope this helps.

like image 39
LordOfThePigs Avatar answered Oct 21 '22 15:10

LordOfThePigs


Check the type of the object returned by Arrays.asList call. I would guess it returns a List<D<? extends C>> object, which will not be castable to a List<D<? extends Object>>.

like image 42
Zed Avatar answered Oct 21 '22 16:10

Zed