为什么在传递 Function 类型参数而不是 Consumer 时,java 中的 for-each 方法不抛出异常?
Why doesn't for-each method in java not throw an exception when a Function type argument is passed instead of Consumer?
为什么在传递 Function 类型参数而不是 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
事实上,在第一行中您有 Consumer,因为 returned 值被忽略了。
但是在第二行中,你 return 明确地得到了结果,所以表达式变成了函数类型。
在第一行中,您提供了一个有效的 Consumer
,它接受一个参数 int a
。这就是 Stream.forEach
所期望的。事实上,消费者将额外 return 一个值并不重要。 returned 值将不会被评估,它将被丢弃。
第二行包含一个 return 是 boolean
的语句。由于此语句具有 return 值,因此它不符合 Consumer
的方法,该方法采用参数但声明为 void
。因此这给出了编译时错误:Void methods cannot return a value
JLS 15.27.2 说:
A block lambda body is void-compatible if every return statement in
the block has the form return;.
因此方法 return 没有明确的 return 值。
关于 return 声明 JLS 14.17. 说:
A return statement with no Expression must be contained in one of the
following, or a compile-time error occurs:
- A method that is declared, using the keyword void, not to return a
value (§8.4.5)
将此应用于第二行会产生以下代码:
Stream.of(1, 2, 3, 4).forEach(a -> { a.equals(1); return; });
补充说明:
通过在第二行 returning equals
的值,这条语句不会变成 Function
。它仍然只是一个声明。
不会抛出异常,因为异常只能在运行时抛出。由于代码无法编译,因此无法执行。正如 JLS 中指定的那样,发出编译时错误。
关于为什么 第一行 有效:the specification 中有解释:
Generally speaking, a lambda of the form () -> expr
, where expr
is a statement expression, is interpreted as either () -> { return expr; }
or () -> { expr; }
, depending on the target type.
上面有下面的例子(好巧,这和你的例子很相似):
// Consumer has a void result
java.util.function.Consumer<String> c = s -> list.add(s);
这只是意味着编译器会忽略表达式的 return 类型,就好像您的代码只是这样(对 void 方法有效):
Stream.of(1, 2, 3, 4).forEach(a -> {
a.equals(1);
});
关于第二行,the spec 说:
A block lambda body is void-compatible if every return statement in the block has the form return;
.
不过,在您的情况下,{return a.equals(1);}
不符合此规则。 Void 方法没有 return 值。
理解这一点的一个简单方法是考虑编译器应用方法主体验证规则(这样主体必须与声明 public void accept(T t)
兼容)——如 tutorial
为什么在传递 Function 类型参数而不是 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
事实上,在第一行中您有 Consumer,因为 returned 值被忽略了。 但是在第二行中,你 return 明确地得到了结果,所以表达式变成了函数类型。
在第一行中,您提供了一个有效的 Consumer
,它接受一个参数 int a
。这就是 Stream.forEach
所期望的。事实上,消费者将额外 return 一个值并不重要。 returned 值将不会被评估,它将被丢弃。
第二行包含一个 return 是 boolean
的语句。由于此语句具有 return 值,因此它不符合 Consumer
的方法,该方法采用参数但声明为 void
。因此这给出了编译时错误:Void methods cannot return a value
JLS 15.27.2 说:
A block lambda body is void-compatible if every return statement in the block has the form return;.
因此方法 return 没有明确的 return 值。
关于 return 声明 JLS 14.17. 说:
A return statement with no Expression must be contained in one of the following, or a compile-time error occurs:
- A method that is declared, using the keyword void, not to return a value (§8.4.5)
将此应用于第二行会产生以下代码:
Stream.of(1, 2, 3, 4).forEach(a -> { a.equals(1); return; });
补充说明:
通过在第二行 returning equals
的值,这条语句不会变成 Function
。它仍然只是一个声明。
不会抛出异常,因为异常只能在运行时抛出。由于代码无法编译,因此无法执行。正如 JLS 中指定的那样,发出编译时错误。
关于为什么 第一行 有效:the specification 中有解释:
Generally speaking, a lambda of the form
() -> expr
, whereexpr
is a statement expression, is interpreted as either() -> { return expr; }
or() -> { expr; }
, depending on the target type.
上面有下面的例子(好巧,这和你的例子很相似):
// Consumer has a void result
java.util.function.Consumer<String> c = s -> list.add(s);
这只是意味着编译器会忽略表达式的 return 类型,就好像您的代码只是这样(对 void 方法有效):
Stream.of(1, 2, 3, 4).forEach(a -> {
a.equals(1);
});
关于第二行,the spec 说:
A block lambda body is void-compatible if every return statement in the block has the form
return;
.
不过,在您的情况下,{return a.equals(1);}
不符合此规则。 Void 方法没有 return 值。
理解这一点的一个简单方法是考虑编译器应用方法主体验证规则(这样主体必须与声明 public void accept(T t)
兼容)——如 tutorial