30

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.

Rann Lifshitz
  • 4,040
  • 4
  • 22
  • 42
ZAHEER AHMED
  • 507
  • 1
  • 5
  • 9
  • Wondering if good old type erasure plays a part here. Casting your lambda invocation as `Function` will compile. – Mena Sep 02 '16 at 14:21
  • 2
    Explicitly setting the lambda parameter type `(Integer i) -> "Test"` also makes it compile. – Konstantin Yovkov Sep 02 '16 at 14:23
  • 2
    Almost duplicate: http://stackoverflow.com/questions/29323520/reference-to-method-is-ambiguous-when-using-lambdas-and-generics – assylias Sep 02 '16 at 14:46
  • 5
    Well, I found [this post on OpenJDK's mailing list](http://openjdk.5641.n7.nabble.com/Lambda-return-types-and-overloaded-methods-td282180.html) that explains this. Still trying to digest. But see *all your implicit lambda cases are ambiguous, because your targets are Predicate and Function - i.e. both are functional types accepting a String (arity 1).* – Tunaki Sep 02 '16 at 14:58
  • 1
    The part about explicitly typed lambda expression in JLS 15.12.2.2 explain why it compiles when using (Integer i) and also why it is ambigous with an implicit lambda expression. – Jean-François Savard Sep 02 '16 at 15:04
  • 4
    @Tunaki Nice ; in conclusion if the lambda is implicitly defined, only the arity will be checked, and since both `Function` and `Predicate` are arity 1, there's confusion. While when we add the `Integer` type, the lambda is explicitly defined, both `Predicate` and `Integer` are deemed applicable considering their arity, but the return type will be inferred from the lambda body and make `Function` the only possible choice. – Aaron Sep 02 '16 at 15:08
  • 7
    Compiling this with the `-Xlint` option displays a warning that sheds some light on this: `LambdaTest1.java:5: warning: [overloads] method1(Predicate) in LambdaTest1 is potentially ambiguous with method1(Function) in LambdaTest1` – VGR Sep 02 '16 at 15:13
  • Have you ever considered giving feedback to one of your 7 questions ? – Jean-François Savard Sep 08 '16 at 15:46

1 Answers1

9

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

Phase 1: Identify Matching Arity Methods Applicable by Strict Invocation

An argument expression is considered pertinent to applicability for a potentially applicable method m unless it has one of the following forms:

  • An implicitly typed lambda expression (§15.27.1).

  • An explicitly typed lambda expression whose body is an expression that is not pertinent to applicability.

  • An explicitly typed lambda expression whose body is a block, where at least one result expression is not pertinent to applicability.

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:

The meaning of an implicitly typed lambda expression or an inexact method reference expression is sufficiently vague prior to resolving a target type that arguments containing these expressions are not considered pertinent to applicability; they are simply ignored (except for their expected arity) until overload resolution is finished.

So using the implicitly typed (i) -> "Test" doesn’t help to decide whether to invoke method1(Predicate<Integer>) or method1(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>) and method1(Predicate<Integer>) is different, as one method’s parameter has a function type with a void 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 a Consumer. Likewise, i -> {} is only void-compatible, thus inappropriate for a Predicate.

There are only a few cases, where the shape is ambiguous:

  • when the block never completes normally, e.g. arg -> { throw new Exception(); } or
    arg -> { for(;;); }
  • when the lambda expression has the form arg -> expression and expression is also a statement. Such expression statements are
    • Assignments, e.g. arg -> foo=arg
    • Increment/decrement expressions, e.g. arg -> counter++
    • Method invocations, as in your example s -> lst.add(s)
    • Instantiations, e.g. arg -> new Foo(arg)

Note that parenthesized expressions are not within this list, so changing s -> lst.add(s) to
s -> (lst.add(s)) is sufficient to turn it into an expression that is not void-compatible anymore. Likewise, turning it into a statement like s -> {lst.add(s);} stops it from being value-compatible. So it’s easy to select the right method in this scenario.

Community
  • 1
  • 1
Holger
  • 285,553
  • 42
  • 434
  • 765
  • this is crazy... let's say I have this `cool(Predicate predicate)` and `cool(Function function)` and calling it like this : `cool(i -> "Test");` This will fail because the first lamba is `implicit lambda`, so i could be anything - BUT for this case it can *only* be integer obviously. If I explicitly say that i is Integer - then it would work. How in the world this is not a bug? It just blows my mind – Eugene Aug 03 '17 at 12:00
  • should I ask a new question about this may be? – Eugene Aug 03 '17 at 12:05
  • @Eugene: I don’t know whether it’s worth another question, as this answer already says it at the beginning, it’s a deliberate cut made by the designers. It might look insane when looking at the example `i -> "Test"` only, but what about `i -> i`? The cut had to be made somewhere and it had to be made in a way that it is possible to describe the cut reasonably, without too many exceptions. – Holger Aug 03 '17 at 13:18
  • yeah... I've read a couple of your answers on the topic and even if I tried (really hard) to understand this - I will probably never do. I could put them in queue of your answers to get to them later - but that queue is already way too big... thank you for the time – Eugene Aug 03 '17 at 13:21