The code below compiles without any error with Eclipse but generates an error with Javac. It seems to be a compiler error but I don't know which one is right.
I would like to point out that I know how to correct this error by changing the code to make it work with both, but that is not the current topic. I would just like to know whether it's a java or an eclipse problem.
I try with Intellij but I have the same javac error.
Sample code to reproduce this error:
import java.util.ArrayList;
import java.util.List;
public class A<T extends B> {
protected List<C> list = new ArrayList<>();
class C {}
public void createIO() {
A<? extends B> x = null;
List<A<? extends B>.C> y = x.list;
}
}
class B {
}
JVM:
openjdk version "13-BellSoft" 2019-09-17
OpenJDK Runtime Environment (build 13-BellSoft+33)
OpenJDK 64-Bit Server VM (build 13-BellSoft+33, mixed mode, sharing)
With Eclipse, I don't have any errors. With Javac, I have the error:
A.java:13: error: incompatible types: List<A<CAP#1>.C> cannot be converted to List<A<? extends B>.C>
List<A<? extends B>.C> y = x.list;
^
where CAP#1 is a fresh type-variable:
CAP#1 extends B from capture of ? extends B
The Eclipse compiler should show a compiler error like javac
does. See Eclipse bug 539105.
javac
behavior is correct and is aligned with the JLS:
And if a generic class
C<T>
has a non-generic member classD
, then the member typeC<String>.D
is a parameterized type, even though the classD
is not generic.
Thus, even though the inner class C
is not generic, the:
A<? extends B>.C
is a parameterized type, where the type parameter of A
is an unknown subtype of B
. So, all rules on Generics apply to C
as well in this context.
Thus, to eliminate compiler error, the type of y
can be declared as follows:
List<? extends A<? extends B>.C> y = x.list;
But keep in mind that this won't allow you to add items to the list y
(see PECS).
If it sounds complicated, try to think about it in scope of a simplified example:
List<Integer> l1 = new ArrayList<>();
List<Number > l2 = l1; // Error: incompatible types...
List<? extends Number> l3 = l1; // OK
Additional details:
The example in the question can be minimized to:
class A<T> {
class C {}
List<C> l1 = null;
List<A<?>.C> l2 = l1; // Error: incompatible types...
List<? extends A<?>.C> l3 = l1; // OK
}
List<A<?>.C>
is a list of C
of A
of unknown type; C
of arbitrary A
can be added:
class A<T> {
class C {}
C c = new C();
void foo(List<A<?>.C> list) {
list.add(new A<String>().c);
list.add(new A<Number>().c);
}
}
See also: