Java 8 Lambda语法更改

我遇到了一个问题,其中Lambda的允许语法在java编译器的1.8.0_05和1.8.0_20(beta)版本之间发生了变化。

例:

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"))); } } 

我想知道的是 – 最后一个例子是有效的Lambda表达式吗? 他们刚刚收紧了编译器validation? 或者最新的1.8编译器中是否有错误?

最新编译器打印的错误是:

 /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 found: (e)->(Syst[...]hi")) reason: argument mismatch; bad return type in lambda expression missing return value 1 error 

编辑(因为我似乎无法在回复中格式化评论):

setOnAction方法的实现是:

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

和EventHandler:

 @FunctionalInterface public interface EventHandler 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); } 

在Java编程语言中,方法调用表达式是一个表达式语句 ,一种可以出现在需要表达式或需要语句的地方的构造。

因此,即使方法返回void也可以使用简化表达式formsparam -> expression用于用例e -> System.out.println("hi") 。 由于此处预期的函数签名是 T -> void ,因此包含单个void方法调用的lambda表达式对此上下文有效。

当您尝试在需要表达式的其他上下文中使用表达式语句时,情况会发生变化。 比较JLS§15.1 :

当且仅当它是调用不返回值的方法的方法调用(第15.12节)时,表达式才表示什么,即声明为void的方法(第8.4节)。 这样的表达式只能用作表达式语句(§14.8),因为表达式可以出现的每个其他上下文都需要表达式来表示某些东西。

正式应用这个规则,即使简单地在(System.out.println("hi"))括号也是无效的,因为这是一个复合表达式,试图在上下文中使用声明为void的方法的方法调用“实际表达“(返回值)是必需的。

所以使用无效表达式的lambda表达式如mi.setOnAction(e -> (System.out.println("hi"))); 也不能有效。 该消息有点误导。 似乎编译器关注的是表单( whatever )表达式是非语句表达式,因此在void上下文中void 。 但是,报告在圆括号中放置void方法调用的初始错误会更有用。

你不能把( … )围绕一个void方法调用的规则没有改变,所以错误是旧的编译器接受这种语法,现在似乎已经修复了。