对于返回void的lambda,这是有效的:
Runnable b = () -> System.out.println("3");
但这不是:
Runnable b = () -> throw new RuntimeException("3");
我知道我可以把它放在一个带大括号的块中,但为什么这不是内联lambda的有效语法呢?
() -> foo
语法将lambda的主体定义为一个表达式(JLS 15.27.2(,因此throw new RuntimeExpression("3")
不会编译是完全合理的:这是一个语句,而不是一个表达式。所以真正的问题是,println为什么要编译?毕竟,这也是一个声明。
JLS 15.27.3说:
如果函数类型的结果为void,则lambda主体要么是语句表达式(§14.8(,要么是void兼容块。
您的两个示例都不是块(与void兼容或其他(,所以它们必须是语句表达式。这些定义见JLS 14.8,包括:
StatementExpression:
Assignment
PreIncrementExpression
PreDecrementExpression
PostIncrementExpression
PostDecrementExpression
MethodInvocation
ClassInstanceCreationExpression
请注意MethodInvocation——这就是让println lambda编译的原因。
简而言之:第一个println编译是因为它是语句表达式的MethodInvocation形式,而throw不编译是因为语句表达式不包括throw
。
注意,所有的语句表达式都是表达式(因此得名(,所以这实际上可以归结为println实际上是void
类型的表达式,尽管我们只将其视为一个语句。(事实上,我们通常将其视为语句的原因是,您只能将其用作语句表达式,如JLS 15.1中所述(