I have the below code:
public class LambdaTest1 {
public static void method1(Predicate<Integer> predicate){
System.out.println("Inside Predicate");
}
public static void method1(Function<Integer,String> function){
System.out.println("Inside Function");
}
public static void main(String[] args) {
method1((i) -> "Test");
}
}
This is giving me an error message as
"The method method1(Predicate) is ambiguous for the type LambdaTest1".
I can see that for the Function
and Consumer
functional interface, the input argument is Integer
. But for the Function
, the return type is String
.
Since my lambda call has the return value of "Text" - This should have called my Function
functional interface instead of throwing this error.
Can anyone please explain the reason behind this behavior?
Also another example:
public class LambdaTest1 {
public static void method1(Consumer<Integer> consumer){
System.out.println("Inside Consumer");
}
public static void method1(Predicate<Integer> predicate){
System.out.println("Inside Predicate");
}
public static void main(String[] args) {
List<Integer> lst = new ArrayList<Integer>();
method1(i -> true);
method1(s -> lst.add(s)); //ambiguous error
}
}
Also In the above code the line method1(s -> lst.add(s));
gives an ambiguos error, but the above line method1(i -> true)
works fine.
As explained in this answer, the Java language designers made a deliberate cut when it comes to process of selecting an overloaded method in combination with type inference. So not every aspect of a lambda expression parameter is used for determining the right overloaded method.
Most notably, in your first example, the lambda expression
(i) -> "Test"
is an implicitly typed lambda expression whose return values are not considered for overload resolution, whereas changing it to, e.g.(Integer i) -> "Test"
will turn it into an explicitly typed lambda expression whose return values are considered. Compare to The Java Language Specification §15.12.2.2.:So explicitly typed lambda expressions can be “pertinent to applicability”, depending on their content, whereas implicitly typed ones are ruled out in general. There is also an addendum, being even more specific:
So using the implicitly typed
(i) -> "Test"
doesn’t help to decide whether to invokemethod1(Predicate<Integer>)
ormethod1(Function<Integer,String>)
and since neither is more specific, the method selection fails before trying to infer the lambda expression’s function type.The other case, selecting between
method1(Consumer<Integer>)
andmethod1(Predicate<Integer>)
is different, as one method’s parameter has a function type with avoid
return and the other’s a non-void
return type, which allows selecting an applicable method via the shape of the lambda expression, which has been already discussed in the linked answer.i -> true
is only value compatible, thus inappropriate for aConsumer
. Likewise,i -> {}
is only void-compatible, thus inappropriate for aPredicate
.There are only a few cases, where the shape is ambiguous:
arg -> { throw new Exception(); }
orarg -> { for(;;); }
arg -> expression
andexpression
is also a statement. Such expression statements arearg -> foo=arg
arg -> counter++
s -> lst.add(s)
arg -> new Foo(arg)
Note that parenthesized expressions are not within this list, so changing
s -> lst.add(s)
tos -> (lst.add(s))
is sufficient to turn it into an expression that is not void-compatible anymore. Likewise, turning it into a statement likes -> {lst.add(s);}
stops it from being value-compatible. So it’s easy to select the right method in this scenario.