当传递函数类型参数而不是Consumer时,为什么Java中的for-each方法不会引发异常?

时间:2018-09-24 07:38:12

标签: java lambda java-8 functional-interface

当传递函数类型参数而不是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

3 个答案:

答案 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的类型。