I've stumbled upon a counterintuitive example of java compiler behavior (in this ru.so question). Given javac 1.8.0_161 and following code:
public class RuSo791351 {
public static void main(String[] args) {
List<Integer> payload = Arrays.asList(1, 2, 3);
Consumer uncheckedConsumer = new Consumer();
uncheckedConsumer.consume(payload);
Consumer<?> wildcardConsumer = new Consumer<>();
wildcardConsumer.consume(payload);
Consumer<Integer> typedConsumer = new Consumer<>();
typedConsumer.consume(payload);
}
private static class Consumer<T> {
public <V> void consume(Collection<V> collection) {
System.out.println("consume(Collection<T>) called");
}
public void consume(List<String> strings) {
System.out.println("consume(List<String>) called");
}
}
}
compiler would be expected to either take type erasure into account and take the easy way, emitting bytecode for consume(List<String>)
call every time, or take the hard way and emit bytecode for consume(Collection<V>)
- again, every time. However, compiler emits consume(List<String>)
for uncheckedConsumer and consume(Collection<V>)
for other cases:
40: invokevirtual #7 // Method me/etki/wtf/so/RuSo791351$Consumer.consume:(Ljava/util/List;)V
...
54: invokevirtual #8 // Method me/etki/wtf/so/RuSo791351$Consumer.consume:(Ljava/util/Collection;)V
...
70: invokevirtual #8 // Method me/etki/wtf/so/RuSo791351$Consumer.consume:(Ljava/util/Collection;)V
So it seems that if i don't specify <T>
parameter for Consumer class at all, compiler ignores all generic information contained in class - even though <V>
is a completely different method parameter - and compiler chooses List over Collection as a more precise type. I'm nearly sure this is an effect of backwards compatibility for pre-generic era code, but couldn't find anything specific in JLS (to be honest, it seems i don't even know how to ask google about this). Can anyone point me to the specification of such behavior?