Method invocation applicability rules confusion

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Mon Dec 11 16:26:59 UTC 2017


Note that in your original example your second argument is:

o -> o.toString()

Whenever you have a lambda (implicit, or explicit) and the target type 
is some inference variable (like T in this case), the lambda is always 
considered potentially applicable (as per 15.12.2.1).

Then, since this is an implicit lambda - implicit lambda don't do much 
in terms of ruling out applicable candidates. This is called pertinence 
to applicability - see 15.12.2.2. An implicit lambda is NOT pertinent to 
applicability, so it doesn't contribute to overload resolution.

Hence, both methods in your example are applicable.

Maurizio


On 11/12/17 14:59, Chris Dennis wrote:
> Forgive me, it’s too long since I was at university, and then I was a 
> physicist, so I just pretended to be good at mathematics/computer 
> science. My intuitive understanding says that f(Consumer<T>, T>) 
> shouldn't be considered applicable given the argument types (as per 
> 15.12.2.3 
> https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2.3). 
>  I’ve tried (and will continue to try) to work through the logic of 
> that section and the accompanying details in Chapter 18, but if you 
> could confirm that it is applicable then that would be great (at least 
> then I’d know that what I’m doing is an academic exercise, and that I 
> should end up proving the method is applicable).
>
> Thanks,
>
> Chris
>
>
>> On Dec 8, 2017, at 4:39 PM, Maurizio Cimadamore 
>> <maurizio.cimadamore at oracle.com 
>> <mailto:maurizio.cimadamore at oracle.com>> wrote:
>>
>> 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
>>
>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/compiler-dev/attachments/20171211/ae1c3d5e/attachment-0001.html>


More information about the compiler-dev mailing list