forEach 参数与 Consumer 功能接口参数不匹配,但代码仍然可以编译,为什么?

forEach parameter does not match Consumer functional interface parameters but code still compiles, why?

我正在为 OCP 考试学习,我注意到以下代码片段,其中在 DoubleStream 上调用的 forEach 方法的参数必须与 DoubleConsumer 功能接口的参数匹配,但是 lambda 与所需的类型不匹配,为什么它仍然可以编译?

  DoubleStream.of(3.14159, 2.71828)
    .forEach(c -> service.submit(
      () -> System.out.println(10*c)
    ));

DoubleConsumer(接受 Double 类型的参数并具有 return 类型的 void),但是此 forEach 具有 return 类型的 Future<?> 其中?表示 Runnable lambda 的 return 类型,它是无效的,Future - 这不是无效的。我这么说是因为 service.submit(...) 的 return 类型是 Future<?> 它不是 void,为什么这段代码可以编译?

lambda 表达式的 return 类型和目标功能接口类型的函数类型的 return 类型必须 完全 匹配。 Java 语言规范指定 void 是一种特殊情况。

§15.27.3中说:

A lambda expression is compatible in an assignment context, invocation context, or casting context with a target type T if T is a functional interface type (§9.8) and the expression is congruent with the function type of the ground target type derived from T.

我们在这里处于调用上下文中。 TDoubleConsumer。从它衍生出来的地面目标类型也是DoubleConsumer,它的函数类型是一个方法,取一个double和returns void.

让我们看看“一致”是什么意思:

A lambda expression is congruent with a function type if all of the following are true:

  • [...]
  • If the lambda parameters are assumed to have the same types as the function type's parameter types, then:
    • If the function type's result is void, the lambda body is either a statement expression (§14.8) or a void-compatible block.

“假定与函数类型的参数类型具有相同的类型”基本上意味着您没有明确写出 c.

的类型

语句表达式就是在末尾加;就可以变成语句的表达式。任何方法调用都是语句表达式。这就是 submit 调用编译的原因。

5不是语句表达式(但它是表达式),所以c -> 5不编译。

如果你考虑一下,通过说 returns something 的方法不应该分配给函数类型为 void [=60= 的函数接口] 类型,你是说“接受 A 并给出 B 的函数”不是一种“A 的消费者”。然而,他们显然A的消费者”!毕竟他们接受了 A。某物是否是 A 的消费者并不取决于他们 生产什么

因此,Java 旨在实现这一点。