javalambdaoverloadingtype-inference

Lambda compatible with two functional interfaces does not give ambiguous method call error on overloaded method, why not?


My overload method is overloaded with 4 different functional interfaces. I have 3 examples in which I call this overloaded method with a lambda that is compatible with 2 of those functional interfaces. In the first example the compiler understandably tells me my method call is ambiguous.

In scenario 2 and 3 however the compiler does not complain... why not?

public class AmbiguityTest {

    private static void overload(Consumer<String> consumer) {
    }

    private static void overload(Function<String, String> function) {
    }

    private static void overload(Runnable runnable) {
    }

    private static void overload(Supplier<String> supplier) {
    }

    public static void testLambda1() {
        //This lambda is compatible with 2 functional interfaces: Consumer<String> and Function<String, String>
        //Calling the overload method gives an "ambiguous method call" error. Makes sense.
        Consumer<String> consumer = ignored -> Instant.now().toString();
        Function<String, String> function = ignored -> Instant.now().toString();
        overload(ignored -> Instant.now().toString());
    }

    public static void testLambda2() {
        //This lambda is also compatible with 2 functional interfaces: Consumer<String> and Function<String, String>
        //Yet here calling the overload method does not give a compile time error. ???WHY NOT???
        Consumer<String> consumer = (String ignored) -> Instant.now().toString();
        Function<String, String> function = (String ignored) -> Instant.now().toString();
        overload((String ignored) -> Instant.now().toString());
    }

    public static void testLambda3() {
        //This lambda is compatible with 2 functional interfaces: Runnable and Supplier<String>
        //Again there is no compile time error although 2 overloaded methods match. ???WHY NO AMBIGUITY???
        Runnable runnable = () -> Instant.now().toString();
        Supplier<String> supplier = () -> Instant.now().toString();
        overload(() -> Instant.now().toString());
    }
}

Solution

  • If more than one method is accessible and applicable, the compiler selects a method, as explained in the Java Language Specification 15.12.2.5. Choosing the Most Specific Method.
    The most relevant part (IMO) for this question is

    A functional interface type S is more specific than a functional interface type T for an expression e if all of the following are true:

    • . . .
    • Let MTS be the function type of the capture of S, and let MTT be the function type of T. ...
    • . . .
    • Let RS be the return type of MTS, adapted to the type parameters of MTT, and let RT be the return type of MTT. One of the following must be true:
      • e is an explicitly typed lambda expression (§15.27.1), and one of the following is true:
        • RT is void.

    As we can see, there is a differentiation between using an explicitly typed lambda (String ignored) ->) and using an implicitly typed one (ignored ->):