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
doIt(x -> (x.toString())); // non-statement expression body
doIt(x -> {x.toString();}); // statement body


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