Java 8 Lambda 语法更改 [英] Java 8 Lambda Syntax change

查看:20
本文介绍了Java 8 Lambda 语法更改的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了一个问题,即在 Java 编译器的 1.8.0_05 和 1.8.0_20(测试版)版本之间,Lambda 的允许语法发生了变化.

示例:

包暂存;导入 javafx.scene.control.MenuItem;公开课测试{公共无效测试(){MenuItem mi = new MenuItem();//这在任何地方编译mi.setOnAction(e -> System.out.println("hi"));//就像这样mi.setOnAction(e -> {System.out.println("hi");});//这不在构建 1.8.0_20-ea-b13 上 - 但在构建 1.8.0_05-b13 上mi.setOnAction(e -> (System.out.println("hi")));}}

我想知道的是 - 最后一个示例是有效的 Lambda 表达式吗?他们刚刚加强了编译器验证?还是最新的1.8编译器有bug?

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

/scratch/src/scratch/Test.java:18: 错误:MenuItem 类中的方法 setOnAction 不能应用于给定类型;mi.setOnAction(e -> (System.out.println("hi")));^需要:EventHandler发现:(e)->(Syst[...]hi"))原因:参数不匹配;lambda 表达式中的错误返回类型缺少返回值1 错误

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

setOnAction 方法的实现是:

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

和事件处理器:

@FunctionalInterface公共接口 EventHandler扩展事件监听器{/*** 当此处理程序所针对的类型的特定事件时调用* 注册发生.** @param event 发生的事件*/无效句柄(T事件);}

解决方案

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

因此您可以使用简化的表达式形式 param ->用例的表达式 e ->System.out.println("hi") 即使该方法返回 void.由于这里预期的函数签名是 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 方法调用周围的规则没有改变,所以错误是旧的编译器接受了这个语法,这似乎现在已经修复.

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

解决方案

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.

这篇关于Java 8 Lambda 语法更改的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆