Overloads will be ambiguous when passing lambda arguments.


The problem

Passing lambdas to an overloaded method may be ambiguous if two overloads have parameters that are functional interfaces with equivalent methods.

Prefer to avoid ambiguous overloads, and consider renaming one of the methods.

For example Function<String, Integer> and IntFunction<String> are both compatible with the lambda x -> x.hashCode().

void f(Function<String, Integer> x) {}
void f(IntFunction<String> x) {}
error: reference to f is ambiguous
    f(x -> x.hashCode());
  both method f(Function<String,Integer>) in Test and method f(IntFunction<String>) in Test match

To avoid the ambiguity, callers will have to use an explicit cast:

f((IntFunction<String>) x -> x.hashCode());
f((Function<String, Integer>) x -> x.hashCode());

Expression-bodied lambdas

The situation is more complicated with expression-bodied lambdas. Consider:

void doIt(Function<String, String> f);
void doIt(Consumer<String> c);

JLS says that lambdas whose body is a statement expression are compatible with functional interfaces whose function type is void-returning or value returning:

A lambda expression (§15.27) is potentially compatible with a functional interface type (§9.8) if all of the following are true:

So, if you have:

doIt(x -> System.gc());

it’s an implicitly typed statement-expression-bodied lambda that’s compatible with both overloads.

Any of the following disambiguate the overloads:

doIt((String x) -> x.toString()); // explicitly typed, calls f(Function)
doIt(x -> (x.toString())); // non-statement expression body, calls f(Function)
doIt(x -> {x.toString();}); // statement body, calls f(Consumer)


Suppress false positives by adding the suppression annotation @SuppressWarnings("FunctionalInterfaceClash") to the enclosing element.