Method reference is ambiguous for Thread.sleep

Winter :

I've come across a weird problem where a method reference to Thread::sleep is ambiguous but a method with the same signature is not.

package test;    

public class Test
{
    public static void main(String[] args)
    {
        foo(Test::sleep, 1000L); //fine
        foo((FooVoid<Long>)Thread::sleep, 1000L); //fine
        foo(Thread::sleep, 1000L); //error
    }

    public static void sleep(long millis) throws InterruptedException
    {
        Thread.sleep(millis);
    }

    public static <P, R> void foo(Foo<P, R> function, P param) {}

    public static <P> void foo(FooVoid<P> function, P param) {}

    @FunctionalInterface
    public interface Foo<P, R> {
        R call(P param1) throws Exception;
    }

    @FunctionalInterface
    public interface FooVoid<P> {
        void call(P param1) throws Exception;
    }
}

I get those 2 errors:

Error:(9, 17) java: reference to foo is ambiguous
  both method <P,R>foo(test.Test.Foo<P,R>,P) in test.Test and method <P>foo(test.Test.FooVoid<P>,P) in test.Test match

Error:(9, 20) java: incompatible types: cannot infer type-variable(s) P,R
    (argument mismatch; bad return type in method reference
      void cannot be converted to R)

The only difference I see is that Thread::sleep is native. Does it change anything? I don't think the overload Thread::sleep(long, int) comes into play here. Why does it happen?

EDIT: Using javac version 1.8.0_111

Erwin Bolwidt :

You can recreate the problem in your own class by adding a method sleep with two arguments to class Test like below:

public static void sleep(long millis) {
}

public static void sleep(long millis, int nanos) {
}

So the problem is really caused by the fact that the method sleep is overloaded.

The JLS indicates that the initial method selection code only looks at the number of type arguments to the functional interface - only in the second phase does it look at the signature of the method inside the functional interface.

JLS 15.13:

It is not possible to specify a particular signature to be matched, for example, Arrays::sort(int[]). Instead, the functional interface provides argument types that are used as input to the overload resolution algorithm (§15.12.2).

(the second-to-last paragraph of this section)

So in the case of Thread::sleep, void sleep(long) potentially matches functional interface FooVoid<P>, while overload void sleep(long, int) potentially matches functional interface Foo<P, R>. That's why you get the "reference to foo is ambiguous" error.

When it tries to go further and see how to match Foo<P, R> with functional method R call(P param1) to the method void sleep(long, int), it finds out that this is not actually possible, and you get another compile error:

test/Test.java:7: error: incompatible types: cannot infer type-variable(s) P,R
        foo(Thread::sleep, 1000L); // error
           ^
    (argument mismatch; bad return type in method reference
      void cannot be converted to R)

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=430595&siteId=1