Method invocation applicability rules confusion

Maurizio Cimadamore maurizio.cimadamore at
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 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.


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:
> 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>)
> 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