我有下面一段显然无法编译的代码:
jshell> static int counter = 0;
counter ==> 0
jshell> Runnable r = () -> counter;
| Error:
| incompatible types: lambda body is not compatible with a void functional interface
| (consider using a block lambda body, or use a statement expression instead)
| Runnable r = () -> counter;
|
这是可以理解的,因为Runnable
的run
签名是void run()
。现在,如果我有以下lambda,它将正常编译:
jshell> static int counter = 0;
counter ==> 0
jshell> Runnable r = () -> counter++;
r ==> $Lambda$23/0x0000000800c0b260@3941a79c
为什么这段代码编译,知道counter++
仍将返回一个整数?
更令人困惑的是,这段代码也可以被编译:
jshell> Supplier<Integer> f = () -> counter++;
f ==> $Lambda$22/0x0000000800c0a410@12edcd21
我试着用Java 17的jshell编译上面的代码。
在Runnable
中,返回值被忽略;主要的部分(为什么它会编译)是其中有一个语句。
它确实返回一个值,所以它也是一个Supplier
。它还操纵counter
(作为副作用),但这对于实现功能接口无关紧要。
(展开评论,附带其他答案和评论)
如果将考虑使用块lambda体解释为"考虑如果使用块lambda体会发生什么",则错误消息本身提供了一些指导。
当您编写无块lambda函数arguments -> something
时,Java仍然会从两个实际块中选择一个:
并且(正如其他答案和评论所指出的)在这种情况下,void somefunction(arguments) { something; }
something;
必须是一个有效的语句
其中sometype somefunction(arguments) { return something; }
something
必须是一个有效的表达式,提供返回的结果。
选择是显式的,您在指定lambda本身的类型时亲自执行。
所以当你写Runnable
时,它有一个单独的void run()
方法,Java试图为你创建它:
这不会工作,因为void run() { counter; }
counter;
不存在于Java
这将不起作用,因为void run() { return counter; }
void
方法不能return
数字。
然而,后者将与Supplier<Integer>
一起工作,Integer get()
只有一个方法:
此方法可以存在(与自动Integer get() { return counter; }
int
->Integer
拳击)。
当您有counter++
时,两者都可以存在,(1.)用于Runnable
,(2.)用于Supplier<Integer>
:
增加class InnerClassForLambda implements Runnable { public void run() { counter++; } }
counter
并且不返回任何内容class InnerClassForLambda implements Supplier<Integer> { Integer get() { return counter++; } }
counter
加1,并返回加1之前的值。
比较
jshell> Runnable r = () -> counter++;
jshell> Runnable r = () -> System.out.println("Hello");
,然后更容易理解为什么前者可以编译。
和System.out.println("Hello");
一样,counter++;
也是一个语句。
现在
Supplier<Integer> f = () -> counter++;
可以编译,因为counter++
没有返回值。