While refactoring code I stumbled upon strange Java compiler (Oracle 1.8.0_101) behavior. My assumption is, if I replace all occurrences of List<T>
by T
(without using any methods from List
) the code should compile, if it compiled before the change.
Now have a look at this code:
interface Mapper<T, U extends MapperProvider<U>> extends Function<T, List<U>> {}
interface MapperProvider<V> {
Mapper<V, ?> provide();
}
private <V extends MapperProvider<V>> void use(V c) {
use2(c.provide().apply(c));
}
private <W extends MapperProvider<W>> void use2(List<W> c) {
}
This code compiles. When I replace List<U>
by U
and List<W>
by W
:
interface Mapper<T, U extends MapperProvider<U>> extends Function<T, U> {}
interface MapperProvider<V> {
Mapper<V, ?> provide();
}
private <V extends MapperProvider<V>> void use(V c) {
use2(c.provide().apply(c));
}
private <W extends MapperProvider<W>> void use2(W c) {
}
it does not compile any more:
Error:(201, 17) java: method use2 in class Test cannot be applied to given types;
required: W
found: capture#1 of ?
reason: inference variable W has incompatible bounds
equality constraints: capture#1 of ?
lower bounds: MapperProvider<capture#1 of ?>
which is correct from my point of view. use2
should be declared to receive MapperProvider<?>
.
Why does the Java compiler treat the first version as legal?
It looks like Oracle thinks that this is a bug. I opened a bug report and Oracle confirmed: JDK-8172222