来自 Lambda 表达式的有效 void Return 语句(示例:Runnable)

Joh*_*her 4 java lambda unary-operator runnable

在 Java 中看到一些关于具有void返回类型的函数式接口的奇怪行为。

有人可以解释为什么task5task6下面的声明编译吗?

public class Test {

    private static int counter;

    private static void testRunnable() {
        /* Runnable has a void return type so the following won't compile.
        * Error message is Void methods cannot return a value. (Java 67108969) Makes sense... */
        // Runnable task1 = () -> { return counter; };
        // Runnable task2 = () -> { return counter + 1; };
        // Runnable task3 = () -> counter;
        // Runnable task4 = () -> counter + 1;

        /* But for some reason, this is totally acceptable. Why? */
        Runnable task5 = () -> counter++;
        Runnable task6 = () -> ++counter;
    }
}
Run Code Online (Sandbox Code Playgroud)

ern*_*t_k 8

lambda 表达式() -> counter++;counter++;语句表达式一样有效。这在JLS 中是明确允许的:

如果函数类型的结果为 void,则 lambda 主体是语句表达式(第 14.8 节)或与 void 兼容的块。

对于语句表达式的定义

通过对表达式求值来执行表达式语句;如果表达式有值,则丢弃该值。

如果您阅读整个 JLS 15.27.3,您就会明白为什么() -> {return counter + 1;}不兼容 void。lambda 主体不使用与简单表达式语句完全相同的规则进行验证。

换句话说,counter++;and++counter;是语句表达式,这意味着该表达式可以被评估并被视为其结果被简单地丢弃的语句。

转换为块体时会更清晰(或者看起来更熟悉):

Runnable task5 = () -> {
    counter++; //result discarded, but you can't use "return counter++;"
};
Runnable task6 = () -> {
    ++counter; //result discarded, but you can't use "return ++counter;"
};
Run Code Online (Sandbox Code Playgroud)

这与 无关UnaryOperator,至少就该功能接口而言。碰巧与counter++兼容IntUnaryOperator,但表达式可能是其他任何内容,包括(但不限于)以下内容,并且您的问题仍然适用,因为该语句会产生结果:

Runnable task5 = () -> counter += 1;
Runnable task6 = () -> counter = counter + 1;
Runnable task7 = () -> Math.addExact(counter, 1); // no change in counter
Runnable task8 = () -> return2(); //return2 takes no input
Run Code Online (Sandbox Code Playgroud)

所有这些都是与Runnable.run(). 具体来说,不task8接受输入但产生结果;它甚至与任何一元运算符功能接口都不兼容。

  • @dreamcrash 我把它写成 `counter++;` 是一个语句表达式。也许我对该元素上的 void 兼容的引用令人困惑,因为 JLS 更关心 void 兼容的块。语句表达式可以用于返回 void 的函数。我来编辑。 (4认同)