为什么等效的 lambda 表达式和方法引用在捕获静态字段值时表现不同?

Why do equivalent lambda expression and method reference behave differently when capturing static field value?

我对 Java lambda 和方法引用行为有点困惑。例如,我们有这个代码:

import java.util.function.Consumer;

public class Main {

    private static StringBuilder sBuilder = new StringBuilder("1");

    public static void main(String[] args) {
        Consumer<String> consumer = s -> sBuilder.append(s);
        sBuilder = new StringBuilder("2");
        consumer.accept("3");
        System.out.println(sBuilder);
    }

}

输出:

23

这按预期工作,但如果我们替换

s -> sBuilder.append(s)

sBuilder::append

输出将是:

2

你知道如何解释这个吗?这不是一样的东西吗?谢谢

在 lambda 表达式中,sBuilder 字段被捕获,但未被计算。它只会在相应的函数接口方法被调用时被评估。那时,sBuilder 引用创建并分配给字段的新实例

sBuilder = new StringBuilder("2");

在方法引用中,立即计算 sBuilder 字段以生成 Consumer 实例。该值引用在静态初始化程序中创建的实例

private static StringBuilder sBuilder = new StringBuilder("1");

并且 Consumer 将对那个进行操作。你打印新的。


来自 Java 语言规范,关于 Run-Time Evaluation of Method References

The body of an invocation method depends on the form of the method reference expression, as follows:

If the form is ExpressionName :: [TypeArguments] Identifier or Primary :: [TypeArguments] Identifier, then the body of the invocation method has the effect of a method invocation expression for a compile-time declaration which is the compile-time declaration of the method reference expression. Run-time evaluation of the method invocation expression is as specified in §15.12.4.3, §15.12.4.4, and §15.12.4.5, where:

  • The invocation mode is derived from the compile-time declaration as specified in §15.12.3.

  • The target reference is the value of ExpressionName or Primary, as determined when the method reference expression was evaluated.

  • The arguments to the method invocation expression are the formal parameters of the invocation method.