This question is actually interesting but not asked clearly, which easily makes people think it is a duplicate.
First, an example which I think most people here should understand why it does not work:
class Foo<T> {}
class Bar<T> {}
class Parent{}
public class Test1
{
public static void main(String[] args) {
Foo<Bar<? extends Parent>> a = null;
Foo<Bar<? extends Object>> b = a; // does not compile
Foo<Bar<?>> c = a; // does not compile
}
}
It is obvious: a Foo<Bar<?>>
/ Foo<Bar<? extends Object>>
is not convertible to Foo<Bar<? extends Parent>>
(To simply: just like you cannot assign List<Dog>
to List<Animal>
reference.)
However, in the question you can see the case of Comparator<I2<?>> B = A;
does compile, which is contradict with what we see above.
The difference, as specified in my example will be:
class Foo<T> {}
class Bar<T extends Parent> {}
class Parent{}
public class Test1
{
public static void main(String[] args) {
Foo<Bar<? extends Parent>> a = null;
Foo<Bar<? extends Object>> b = a; // does not compile
Foo<Bar<?>> c = a; // COMPILE!!
}
}
By changing class Bar<T>
to class Bar<T extends Parent>
created the difference. It is a pity that I still cannot find the JLS section that is responsible on this, but I believe it when you declare a generic class with bound in type parameter, the bound is implicitly carried to any wildcard you use against that.
Therefore, the example become:
class Foo<T> {}
class Bar<T extends Parent> {}
class Parent{}
public class Test1
{
public static void main(String[] args) {
Foo<Bar<? extends Parent>> a = null;
//...
Foo<Bar<? extends Parent>> c = a;
}
}
which explain why this compile.
(Sorry I cannot find evidence this is how the language is designed. It will be great if someone can find in JLS for this)