I have problems understanding the differences between the following two method signatures.
abstract class Example {
void test() {
Class<? extends Parent<? extends Child>> clazz = null;
works(clazz);
error(clazz); // Error
}
abstract <T extends Child> Parent<T> works(Class<? extends Parent<? extends T>> clazz);
abstract <T extends Child> Parent<T> error(Class<? extends Parent<T>> clazz);
interface Child {}
interface Parent<U extends Child> {}
}
Compiling this code gives the following error (Tested with 1.8.0_271, 11.0.9 and 15.0.1).
…/src/main/java/Example.java:6:5
java: method error in class Example cannot be applied to given types;
required: java.lang.Class<? extends Example.Parent<T>>
found: java.lang.Class<capture#1 of ? extends Example.Parent<? extends Example.Child>>
reason: cannot infer type-variable(s) T
(argument mismatch; java.lang.Class<capture#1 of ? extends Example.Parent<? extends Example.Child>> cannot be converted to java.lang.Class<? extends Example.Parent<T>>)
Why is ? extends
needed? T already extends Child in the type parameter (T extends Child
) and this additional ? extends
seems redundant to me.
Update:
Starting javac with -DverboseResolution=all
is a bit more descriptive but still confusing
…src/main/java/Example.java:6: error: method error in class Example cannot be applied to given types;
error(clazz); // Error
^
required: Class<? extends Parent<T>>
found: Class<CAP#1>
reason: cannot infer type-variable(s) T
(argument mismatch; Class<CAP#1> cannot be converted to Class<? extends Parent<T>>)
where T is a type-variable:
T extends Child declared in method <T>error(Class<? extends Parent<T>>)
where CAP#1 is a fresh type-variable:
CAP#1 extends Parent<? extends Child> from capture of ? extends Parent<? extends Child>
Interpreting this:
Required is Class<? extends Parent<T>>
. T
is extends Child
. So let's put this together into Class<? extends Parent<? extends Child>>
.
Found is Class<CAP#1>
. CAP#1
is extends Parent<? extends Child>
. Together this gives me Class<? extends Parent<? extends Child>>
So both signatures are the same?!?
Update:
Compiling this code in Eclipse works. In contrast to javac the Eclipse compiler treats the signatures as the same.
The really long explanation I did for myself too, is here; if you want to read it. Plus this is related to capture conversion.
In short, the compiler simply can't prove that a wildcard capture is compatible with T
, in your case. If you run with: javac --debug=verboseResolution=all ...
, you will see an error like:
....
(argument mismatch; Class<CAP#1> cannot be converted to Class<? extends Parent<T>>)
So a capture conversion (CAP#1
) can't simply satisfy the compiler here. The way to make it work is to introduce one more capture, via ...<? extends T>
. This can be proven by the compiler that ? extends Child
is <:
(notation explained in the second link) of ? extends T
; thus covariance is established.
It is also said that a "bounded wildcard makes the type covariant".
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With