Java 8 lambda语法中什么时候可以省略括号?

19

我知道Java 8的lambda实现可能会发生变化,但在lambda构建b39中,我发现当lambda表达式返回非void类型时,括号才可以省略。例如,下面的代码可以编译通过:

public class Collections8 {
        public static void main(String[] args) {
                Iterable<String> names = Arrays.asList("Alice", "Bob", "Charlie");
                names.filter(e -> e.length() > 4).forEach(e -> { System.out.println(e); });
        }
}

但是像这样删除大括号:
names.filter(e -> e.length() > 4).forEach(e -> System.out.println(e));

出现错误

Collections8.java:6: error: method forEach in interface Iterable<T> cannot be applied to given types;
        names.filter(e -> e.length() > 4).forEach(e -> System.out.println(e));
                                         ^
  required: Block<? super String>
  found: lambda
  reason: incompatible return type void in lambda expression
  where T is a type-variable:
    T extends Object declared in interface Iterable

有人能解释一下这里发生了什么吗?

1
如果它仍在开发中并且可能会更改,为什么要问呢?答案可能会立即过时。 - user12345613
11
因为我想了解背后的推理。 - hertzsprung
1
你从哪里获取filter方法?我有Java 8 Lambda版本,但是你的代码无法编译。 - NimChimpsky
@NimChimpsky:你有lambda b39吗?由于Lambda API仍在变动中,因此方法可能会发生变化。 - hertzsprung
3
现在看来这个问题似乎已经无意义了,因为在Java 8的实际发布版本中,问题中的这两个示例都被编译器接受并能正常工作。第二个lambda甚至可以被一个方法引用所代替。 - LordOfThePigs
显示剩余4条评论
4个回答

23

当 Lambda 的主体是单个表达式或 void 方法调用时,可以省略大括号。每个表达式都会求值为一个值,因此不能是 void。

如果 Lambda 主体是一系列语句的块(例如多个计算语句后跟一个 return 语句),或者 Lambda 没有返回值(即具有 void 返回类型)且不是单个 void 方法调用,则必须使用块形式,并使用大括号括起来。

在块形式的 Lambda 中,如果存在 return 值,那么所有可能的代码路径都必须要么return一个值,要么抛出一个 Throwable


1
我认为这只是重复了原帖的内容并添加了一些额外的细节,没有回答所要求的“为什么”,除非我漏掉了什么。 - Kevin Welker
我认为这为我澄清了事情。语句不是表达式,因为它们不能被评估。如果lambda体是一个表达式,括号是可选的,但如果体是一个语句或一系列语句,则它们是强制性的。这听起来对吗,@jpm? - hertzsprung
2
几乎。应该澄清的是,表达式是语句的一种类型(即所有表达式都是语句,但并非所有语句都是表达式)。还要注意的是,块(用 {} 包围的代码)在语法上是单个语句。更清楚地说,lambda 的主体可能包含一个单独的语句,它可以是表达式或块语句。 - jpm
似乎已经有所改变,Lambda 教程 现在声明:"在 lambda 表达式中,你必须用花括号({})将语句括起来。然而,如果是 void 方法的调用则可以不用花括号括起来。例如,以下是一个有效的 lambda 表达式:email -> System.out.println(email)" - meyertee
似乎带有返回值的方法调用也可以不使用大括号。public interface ReturnString { String get(String s); } `public class Main { public static void main(String[] args) { ReturnString returnString = s -> new Object().toString(); System.out.println(returnString.get("")); }}` - Boreas320

11

最新消息:EG(ECMA委员会)已经(大部分)就语法做出了决定。

After considering a number of alternatives, we decided to essentially adopt the C# syntax. We may still deliberate further on the fine points (e.g., thin arrow vs fat arrow, special nilary form, etc), and have not yet come to a decision on method reference syntax.

The C# syntax is:

lambda = ArgList Arrow Body
ArgList = Identifier
           | "(" Identifier [ "," Identifier ]* ")"
           | "(" Type Identifier [ "," Type Identifier ]* ")"
Body = Expression
           | "{" [ Statement ";" ]+ "}"
一个表达式会被计算成某个值,在Java中不能有void表达式。它是一条语句,因此需要在其周围加上{}

http://mail.openjdk.java.net/pipermail/lambda-dev/2011-September/003936.html


2

如果lambda表达式中没有花括号,它会自动返回->运算符后的一个表达式。
因此,当你有一个不返回任何内容的lambda表达式时,你必须使用花括号。


2

我尝试了你的代码,对于最新的JRE版本,我认为它是可以使用的。

以下是我从Oracle Java文档中引用的内容。

In a lambda expression, you must enclose statements in braces ({}). However, you do not have to enclose a void method invocation in braces.
For example, the following is a valid lambda expression:

email -> System.out.println(email)

这份文档解释得非常清楚。

参考资料:https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html


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