当传递函数类型参数而不是Consumer时,为什么Java中的forEach
方法不显示编译器错误?这两行都为流中的每个元素返回一个布尔值,但是只有第二行会出现编译错误?在这种情况下, lambda表达式是否还有其他属性?
这是我的代码:
Stream.of(1,2,3,4).forEach(a->a.equals(1));//line 1
Stream.of(1,2,3,4).forEach(a->{return a.equals(1);});//line 2
答案 0 :(得分:3)
在第一行中,您提供了一个有效的Consumer
,该参数带有一个自变量int a
。这就是Stream.forEach
的期望。消费者将额外返回值这一事实并不重要。返回的值将不被评估,将被丢弃。
第二行包含一个返回boolean
的语句。由于该语句具有返回值,因此它不符合采用参数但声明为Consumer
的{{1}}方法。因此,这产生了编译时错误:无效方法无法返回值
JLS 15.27.2说:
如果lambda主体中的每个return语句均与void兼容 该块具有形式return;。
因此,该方法返回时没有明确的返回值。
关于返回语句JLS 14.17.说:
其中一个表达式中必须包含不带表达式的return语句 以下,或发生编译时错误:
- 使用关键字void声明的方法,不返回 值(第8.4.5节)
将此内容应用于第二行将导致以下代码:
void
更多说明:
通过在第二行中返回Stream.of(1, 2, 3, 4).forEach(a -> { a.equals(1); return; });
的值,该语句不会成为equals
。仍然只是一个声明。
不会引发异常,因为只能在运行时引发异常。由于代码无法编译,因此无法执行。按照JLS中的规定,会发出编译时错误。
答案 1 :(得分:2)
关于第一行的工作原理:the specification中有一个解释:
通常来说,
() -> expr
形式的lambda(其中expr
是语句表达式)根据目标类型而解释为() -> { return expr; }
或() -> { expr; }
。
上面带有以下示例(很好的巧合,这与您的示例非常相似):
// Consumer has a void result
java.util.function.Consumer<String> c = s -> list.add(s);
这只是意味着编译器会忽略表达式的返回类型,就像您的代码就是这个(对于void方法有效)一样:
Stream.of(1, 2, 3, 4).forEach(a -> {
a.equals(1);
});
第二行,the spec说:
如果块中的每个return语句的格式均为
return;
,则lambda主体是无效的。
但是,对于您而言,{return a.equals(1);}
不符合此规则。无效方法不会返回值。
一种简单的理解方法是考虑编译器应用方法主体验证规则(这样主体必须与声明public void accept(T t)
兼容),如tutorial
答案 2 :(得分:1)
实际上,在第一行中,您有Consumer,因为返回值被忽略。 但是在第二行中,您显式返回结果,因此表达式成为Function的类型。