java 为什么下面的lambda在赋值给Runnable时会编译?

qvtsj1bj  于 2023-01-01  发布在  Java
关注(0)|答案(2)|浏览(134)

我有下面的一段代码,显然不能编译:

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;
|

这是可以理解的,因为Runnablerun的签名是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

我尝试使用Java17的jshell编译上面的代码。

8zzbczxx

8zzbczxx1#

Runnable中,忽略返回值;最主要的部分(为什么要编译)是因为其中有一条语句。
它确实返回一个值,所以它也是一个Supplier,它也操作counter(作为一个副作用),但这对实现函数接口无关紧要。

oiopk7p5

oiopk7p52#

(扩展评论,借用其他答案和评论)
如果您将 consider using a block lambda body 解释为“consider what would happened if you used a block lambda body”(考虑如果您使用了块lambda body会发生什么),那么错误消息本身就提供了一些指导。
当你编写一个无块lambda函数arguments -> something时,Java仍然会从两个实际的块中选择一个:
1.

void somefunction(arguments) {
    something;
}

和(如其他答案和注解所指出)在这种情况下something;必须是有效语句
1.

sometype somefunction(arguments) {
    return something;
}

,其中something必须是有效表达式,提供要返回的结果。
选择是显式的,您可以在指定lambda本身的类型时亲自进行选择。
因此,当你编写Runnable时,它只有一个void run()方法,Java会尝试为你创建它:
1.

void run() {
    counter;
}

这不会起作用,因为counter;在Java中不存在
1.这是行不通的,因为void方法不能return一个数字。
然而,后一个方法可以与Supplier<Integer>一起使用,Supplier<Integer>只有一个Integer get()方法:
1.

Integer get() {
    return counter;
}

此方法可以存在(使用自动int-〉Integer装箱)。
当你有counter++时,两者都可以存在,(1.)对于Runnable,(2.)对于Supplier<Integer>
1.

class InnerClassForLambda implements Runnable {
    public void run() {
        counter++;
    }
}

递增counter,不返回任何值
1.

class InnerClassForLambda implements Supplier<Integer> {
    Integer get() {
        return counter++;
    }
}

递增counter并返回递增前的值。

相关问题