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());
}
}
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 typeT
for an expressione
if all of the following are true:
- . . .
- Let
MTS
be the function type of the capture ofS
, and letMTT
be the function type ofT
. ...- . . .
- Let
RS
be the return type ofMTS
, adapted to the type parameters ofMTT
, and letRT
be the return type ofMTT
. 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
isvoid
.
As we can see, there is a differentiation between using an explicitly typed lambda (String ignored) ->
) and using an implicitly typed one (ignored ->
):
void
(e.g. Consumer
and Runnable
) is considered less specific than a non-void
one (Function
and Supplier
);void
is not considered.