4

I ran into an issue where the allowed syntax of a Lambda has changed between versions 1.8.0_05 and 1.8.0_20 (beta) of the java compiler.

Example:

package scratch;

import javafx.scene.control.MenuItem;

public class Test
{
    public void test()
    {
        MenuItem mi = new MenuItem();

        //This compiles anywhere
        mi.setOnAction(e -> System.out.println("hi"));

        //as does this
        mi.setOnAction(e -> {System.out.println("hi");});

        //This doesn't on build 1.8.0_20-ea-b13 - but does on build 1.8.0_05-b13
        mi.setOnAction(e -> (System.out.println("hi")));
    }
}

What I would like to know - is the last example a valid Lambda expression? And they have just tightened the compiler validation? Or is there a bug in the latest 1.8 compiler?

The error printed by the latest compiler is:

/scratch/src/scratch/Test.java:18: error: method setOnAction in class MenuItem cannot be applied to given types;
                mi.setOnAction(e -> (System.out.println("hi")));
                  ^
  required: EventHandler<ActionEvent>
  found: (e)->(Syst[...]hi"))
 reason: argument mismatch; bad return type in lambda expression
      missing return value
1 error

Edit (since I can't seem to format comments in replies):

The implementation of the setOnAction method is:

public final void setOnAction(EventHandler<ActionEvent> value) {
    onActionProperty().set( value);
}

And EventHandler:

@FunctionalInterface
public interface EventHandler<T extends Event> extends EventListener {
    /**
     * Invoked when a specific event of the type for which this handler is
     * registered happens.
     *
     * @param event the event which occurred
     */
    void handle(T event);
}
2
  • what is the return type of menuItem.setOnAction() method in your new classpath ? Commented Jun 10, 2014 at 16:38
  • @user2163960, can you please add in your question the definition of: MenuItem.setOnAction(), and the interface it receives as a parameter. If the method is overloaded please include all overloading methods. Thanks Commented Jun 10, 2014 at 17:19

1 Answer 1

14

In the Java programming language, a method invocation expression is an Expression Statement, a construct which can appear at both places, where an expression is required or where a statement is required.

Therefore you can use the simplified expression form param -> expression for the use case e -> System.out.println("hi") even if the method returns void. Since the function signature expected here is <T extends Event> T -> void, your lambda expression containing a single invocation of a void method is valid for this context.

Things change when you are trying to use the Expression Statement in a different context where an expression is required. Compare JLS §15.1:

An expression denotes nothing if and only if it is a method invocation (§15.12) that invokes a method that does not return a value, that is, a method declared void (§8.4). Such an expression can be used only as an expression statement (§14.8), because every other context in which an expression can appear requires the expression to denote something.

Applying this rule formally, even simply putting braces around it like in (System.out.println("hi")) is invalid as this is a compound expression trying to use the method invocation of a method declared void in a context where a “real expression” (returning a value) is required.

And so the lambda expression using the invalid expression as in mi.setOnAction(e -> (System.out.println("hi"))); can’t be valid either. The message is a bit misleading. It seems that the compiler focuses on the fact that an expression of the form ( whatever ), is a non-statement expression and therefore can’t be valid in a void context. However, reporting the initial error of putting a void method invocation in round braces would be more useful.

The rule that you can’t put ( … ) around a void method invocation didn’t change, so the error was the older compiler accepting this syntax which seems to have been fixed now.

Sign up to request clarification or add additional context in comments.

3 Comments

Do you have any clue whether this was an intended change, or perhaps a side effect of another bugfix?
I think, it is related to this issue which has been fixed in 8u20-b13 (see “Should always use lambda body structure to disambiguate overload resolution”)
Direct link to bug report is here: JDK-8029718

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.