Back to Error Prone

FunctionalInterfaceClash

docs/bugpattern/FunctionalInterfaceClash.md

2.49.02.2 KB
Original Source

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().

java
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:

java
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:

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

JLS 15.12.2.1 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:

  • The arity of the target type's function type is the same as the arity of the lambda expression.

  • If the target type's function type has a void return, then the lambda body is either a statement expression (§14.8) or a void-compatible block (§15.27.2).

  • If the target type's function type has a (non-void) return type, then the lambda body is either an expression or a value-compatible block (§15.27.2).

So, if you have:

java
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)