OCP备考疑问:DoubleStream.forEach的lambda返回Future为何仍可编译?
编译通过的核心原因:Java 针对 void 目标函数式接口的表达式兼容规则
先理清类型校验的前提
首先明确这段代码涉及的两个核心类型定义:
DoubleStream.forEach方法的形参要求实现DoubleConsumer函数式接口DoubleConsumer的唯一抽象方法签名为void accept(double value),目标返回值严格为void
你产生疑惑的核心误区,是默认把「lambda 主体的返回值类型必须和目标接口的返回值完全一致」的规则套用到了所有场景,但这条规则在目标返回值为 void 时会触发专门的兼容逻辑。
具体的语法兼容规则
当函数式接口的目标抽象方法返回值为 void 时,Java 编译器对 lambda 主体的校验规则是:
- 只要 lambda 主体是合法的语句表达式,无论这个表达式本身是否有返回值、返回值是什么类型,都属于合法实现
- 编译器会直接丢弃语句表达式的返回值,不会因为表达式有非 void 返回值抛出类型不匹配错误
对应到你这段代码里:
c -> service.submit(() -> System.out.println(10*c))
lambda 主体的核心是调用 service.submit(...) 方法,虽然这个方法的返回值是 Future<?>,但因为匹配的目标接口 DoubleConsumer.accept 的返回值是 void,编译器会直接把 submit 生成的 Future 对象丢弃,将整个 lambda 判定为合法的 DoubleConsumer 实现,完全符合编译要求。
对照常见的日常编码场景你会更熟悉这个规则:我们平时写
Runnable r = () -> list.add("test")也是完全合法的——哪怕List.add()方法本身返回boolean,也不会因为返回值非 void 报错,本质和这段 stream 代码的编译逻辑完全一致。
容易被混淆的边界
只有当目标函数式接口的返回值不是 void 时,才会严格校验返回值的类型匹配:
- 如果接口要求返回
T类型(比如Supplier<T>),那么 lambda 必须返回可赋值为T类型的值,哪怕 lambda 主体是一个 void 方法调用,也会直接触发编译错误 - 这也是 OCP 考试中经常出现的干扰考点:考生容易把两种返回值场景的校验规则搞混,错判有返回值的方法调用无法传入要求 void 返回的接口。
内容的提问来源于stack exchange,提问作者RamanSB
相关产品推荐
相关产品推荐

