为什么lambda体中的throw语句必须用完整的代码块括起来?

38

如果在 lambda 函数中只有一个语句,我们可以省略为其定义完整的代码块:

new Thread(() -> System.out.println());

为什么抛出异常的语句不是这种情况呢?这会导致编译错误,指出'{' expected

new Thread(() -> throw new RuntimeException());

当然,将lambda体封装在代码块中也是可以的:

new Thread(() -> {
    throw new RuntimeException();
});

我不是完全确定,但我认为省略块会使Java隐式地返回表达式的结果。像return System.out.println()这样的语句是有效的,而return throw new RuntimeException()则无效。 - Neutrosider
3
"Runnable"不返回任何内容,尤其是"void"。 - Jacob G.
1
你为什么认为“需要一个规范的答案来解决所有问题”?这些问题不是已经在像这个这个这个这个这个那个的问答中得到了解答吗? - Holger
1
@Holger 很有用的链接,谢谢,虽然我自己找不到它们(特别是第一个链接,似乎是完全重复的)。但是,它们似乎也缺乏JLS中所做决策背后的理由,我认为这对于更好地理解功能会很有帮助。 manouti回答中提供的讨论链接也很有帮助。 - Dragan Bozanovic
3个回答

25
据我所知,jls规定Lambda函数体必须是一个表达式或一个代码块。像这样写是不行的:
new Thread(() -> throw new RuntimeException());

如果一个变量既不是整数也不是字符串,编译器会以某种方式告知你。

声明方式如下:

 new Thread(() -> {
     throw new RuntimeException();
 });

这使它成为一个块。以下是相关部分:

块是用花括号包含的一系列语句、局部类声明和局部变量声明语句。


对于这个答案的点赞有些困惑 - OP 问为什么 JLS 是这样写的。OP 没有给出超链接,但这个答案给出了一个,但是这个答案甚至没有提出一个可能的答案来回答“为什么”,而这正是问题所在。 - JoeG
2
@JoeG 我自己也没有想到会有这么多赞,我承认。原帖的问题是为什么这个代码不能编译,而不是为什么JLS要写成这样,至少在我看来是这样的。 - Eugene
@JoeG 如果问题是为什么JLS被写成那样,Holger提供了所需的链接。 - Eugene
谢谢 - 我相信你理解我没有冒犯的意思!我只是期望得到一份讨论Fork/Join线程池以及捕获任何这样抛出的异常的答案。为了实现这个目标,我相信选择了这种语法,因此编译器强制执行... - JoeG

21
在Java8中,lambda body的语法仅接受表达式。而抛出异常是一个语句,而不是一个表达式
throwStatement: 'throw' expression ';' ;

lambdaBody: expression | block;

expression: lambdaExpression | assignmentExpression;

block : '{' blockStatements? '}' ;

如果需要的话,也许在下一个JDK版本中将throwStatement包括在lambdaBody中可以增强它。正如你上面提到的那样,我们确实需要它。例如:

lambdaBody: expression | block | throwStatement;

15

throw语句是一种语句而不是表达式,因此必须放在大括号内。根据这篇文章,Java专家小组曾就Lambda表达式的语法进行了非正式调查,并提出了四个选项:

  • 稻草人(Strawman)#(arglist)(expr)和#(arglist){statements}
  • BGGA{ args -> statements }(类似于Scala和Groovy)
  • SotL#{ args -> statements}
  • Redmond(args) -> { statements }

最终,根据这篇邮件,选择采用类似于C#的语法,与上面的最后一个选项最接近。在C#中,有“表达式Lambda”和“语句Lambda”的区别:

表达式Lambda(C#):

(input parameters) => expression

语句 lambda (C#):

(input parameters) => {statement;}  

这个语法在这个MSDN文档页面中有详细解释。

选择此语法而不是其他选项的原因在上一个讨论线程中提到:

选择这种语法的决定是双重的:

  • 从大多数主观标准来看,这种语法得分还不错(尽管有些情况看起来很糟糕,就像其他所有语法一样)。 特别是,它对作为方法参数使用的“小”lambda表达式以及对大型(多语句)lambda表达式都表现良好。

  • 尽管进行了广泛搜索,但在备选项中没有明确的优胜者(每种形式都有一些优点和一些不太好的方面,而且没有一种形式明显比其他形式更好)。 因此,我们认为最好选择已经被证明在两种最像Java的语言——C#和Scala中表现良好的东西,而不是发明新的东西。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接