Method invocation applicability rules confusion
Maurizio Cimadamore
maurizio.cimadamore at oracle.com
Fri Dec 8 21:39:31 UTC 2017
This is a tricky one - I'll try to explain what's happening :-)
FIrst, let's leave lambdas on the side - this has nothing to do with it.
And also simplify the program as follows (I've also alpha-renamed the
type vars):
static <T> void f(Consumer<T> c, T o) { }
static <Z> void f(Consumer<Z> c, String s) { }
And consider the following invocation:
f(null, null)
Now, looking at the available signatures, one might be tempted to
conclude that the second signature is most specific. But that's not how
the rules in JLS 15.12.2.5 work - those rule say that we need to do two
applicability tests:
1) is f(Consumer<T>, T) applicable given argument types { Consumer<Z>,
String } ?
2) is f(Consumer<Z>, String) applicable given argument types {
Consumer<T>, T } ?
If the answer is yes to either (1) or (2), that that's the most specific
method; if (1) and (2) are both true/false then the callsite is ambiguous.
So, let's process the two questions:
For (1) we have the following applicability tests (note that _only_ Z
acts as an an inference variable in this test)
Consumer<T> <: Consumer<Z>
T <: String
now, no matter what we infer for Z, T<: String is always going to be
false. So (1) is not satisfied. Let's move on to (2).
For (2) we have the following applicability tests (this time _only_ T
acts as an inference variable)
Consumer<Z> <: Consumer<T>
String <: T
This looks more interesting. From the first constraint we derive:
Z = T
But this means that, if we incorporate bounds,
String <: T, T = Z --> String <: Z
Which is, again, false.
So, unfortunately, both applicability tests (1) and (2) fails, so
neither method is more specific. Hence the ambiguity error.
Cheers
Maurizio
On 08/12/17 17:34, Chris Dennis wrote:
> Hi All,
>
> I’ve hit an interesting issue with an API that I am responsible for whereby the equivalent to the following does not compile:
>
> public class TargetTypingWeirdness {
>
> public static void test() {
> Consumer<String> stringConsumer = System.out::println;
>
> f(stringConsumer, o -> o.toString());
> }
>
> static <T> Runnable f(Consumer<T> c, T t) {
> return () -> c.accept(t);
> }
>
> static <T> Consumer<Object> f(Consumer<T> c, Function<Object, T> ft) {
> return o -> c.accept(ft.apply(o));
> }
> }
>
> with:
>
> TargetTypingWeirdness.java:9: error: reference to f is ambiguous
> f(stringConsumer, o -> o.toString());
> ^
> both method <T#1>f(Consumer<T#1>,T#1) in TargetTypingWeirdness and method <T#2>f(Consumer<T#2>,Function<Object,T#2>) in TargetTypingWeirdness match
> where T#1,T#2 are type-variables:
> T#1 extends Object declared in method <T#1>f(Consumer<T#1>,T#1)
> T#2 extends Object declared in method <T#2>f(Consumer<T#2>,Function<Object,T#2>)
> TargetTypingWeirdness.java:9: error: incompatible types: cannot infer type-variable(s) T
> f(stringConsumer, o -> o.toString());
> ^
> (argument mismatch; String is not a functional interface)
> where T is a type-variable:
> T extends Object declared in method <T>f(Consumer<T>,T)
> 2 errors
>
> I’m trying to figure out if this is an allowed behavior under the spec, or a bug in javac?
>
> Any help greatly appreciated,
>
> Chris
More information about the compiler-dev
mailing list