Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lambda expression and method overloading doubts

OK, so method overloading is-a-bad-thing™. Now that this has been settled, let's assume I actually want to overload a method like this:

static void run(Consumer<Integer> consumer) {     System.out.println("consumer"); }  static void run(Function<Integer, Integer> function) {     System.out.println("function"); } 

In Java 7, I could call them easily with non-ambiguous anonymous classes as arguments:

run(new Consumer<Integer>() {     public void accept(Integer integer) {} });  run(new Function<Integer, Integer>() {     public Integer apply(Integer o) { return 1; } }); 

Now in Java 8, I'd like to call those methods with lambda expressions of course, and I can!

// Consumer run((Integer i) -> {});  // Function run((Integer i) -> 1); 

Since the compiler should be able to infer Integer, why don't I leave Integer away, then?

// Consumer run(i -> {});  // Function run(i -> 1); 

But this doesn't compile. The compiler (javac, jdk1.8.0_05) doesn't like that:

Test.java:63: error: reference to run is ambiguous         run(i -> {});         ^   both method run(Consumer<Integer>) in Test and         method run(Function<Integer,Integer>) in Test match 

To me, intuitively, this doesn't make sense. There is absolutely no ambiguity between a lambda expression that yields a return value ("value-compatible") and a lambda expression that yields void ("void-compatible"), as set out in the JLS §15.27.

But of course, the JLS is deep and complex and we inherit 20 years of backwards compatibility history, and there are new things like:

Certain argument expressions that contain implicitly typed lambda expressions (§15.27.1) or inexact method references (§15.13.1) are ignored by the applicability tests, because their meaning cannot be determined until a target type is selected.

from JLS §15.12.2

The above limitation is probably related to the fact that JEP 101 wasn't implemented all the way, as can be seen here and here.

Question:

Who can tell me exactly what parts of the JLS specifies this compile-time ambiguity (or is it a compiler bug)?

Bonus: Why were things decided this way?

Update:

With jdk1.8.0_40, the above compiles and works fine

like image 994
Lukas Eder Avatar asked May 02 '14 14:05

Lukas Eder


People also ask

What are the disadvantages of lambda expression?

The big paradox of lambdas is that they are so syntactically simple to write and so tough to understand if you're not used to them. So if you have to quickly determine what the code is doing, the abstraction brought in by lambdas, even as it simplifies the Java syntax, will be hard to understand quickly and easily.

What are the two rules for overloading methods?

Method Overloading Rules Two methods will be treated as overloaded if both follow the mandatory rules below: Both must have the same method name. Both must have different argument lists.

Does lambda expression improve performance?

Oracle claims that use of lambda expressions also improve the collection libraries making it easier to iterate through, filter, and extract data from a collection. In addition, new concurrency features improve performance in multicore environments.

Why method reference is better than lambda?

The method references can only be used to replace a single method of the lambda expression. A code is more clear and short if one uses a lambda expression rather than using an anonymous class and one can use method reference rather than using a single function lambda expression to achieve the same.


1 Answers

I think you found this bug in the compiler: JDK-8029718 (or this similar one in Eclipse: 434642).

Compare to JLS §15.12.2.1. Identify Potentially Applicable Methods:

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

Note the clear distinction between “void compatible blocks” and “value-compatible blocks”. While a block might be both in certain cases, the section §15.27.2. Lambda Body clearly states that an expression like () -> {} is a “void compatible block”, as it completes normally without returning a value. And it should be obvious that i -> {} is a “void compatible block” too.

And according to the section cited above, the combination of a lambda with a block that is not value-compatible and target type with a (non-void) return type is not a potential candidate for the method overload resolution. So your intuition is right, there should be no ambiguity here.

Examples for ambiguous blocks are

() -> { throw new RuntimeException(); } () -> { while (true); } 

as they don’t complete normally, but this is not the case in your question.

like image 199
Holger Avatar answered Sep 19 '22 17:09

Holger