为什么在工厂方法和方法引用(单例/原型)中使用 lambda 时函数接口初始化不同?

koz*_*zmo 7 java lambda equals functional-interface method-reference

我有两种生产“消费者”的工厂方法使用不同的方法 lambda 和方法引用:

@SuppressWarnings("Convert2MethodRef")
public Consumer<String> lambdaPrintStringConsumer(){
    return x -> System.out.println(x);
}

public Consumer<String> methodRefPrintStringConsumer(){
    return System.out::println;
}
Run Code Online (Sandbox Code Playgroud)

我发现在第一种情况 ( lambdaPrintStringConsumer()) 中,方法返回对同一对象的引用

@Test
public void shouldSameFromFactoryMethod_lambda() {
    Consumer<String> consumerA = lambdaPrintStringConsumer();
    Consumer<String> consumerB = lambdaPrintStringConsumer();
    
    Assert.assertSame(consumerA, consumerB);//consumerA == consumerB --> true
}
Run Code Online (Sandbox Code Playgroud)

但是在第二个 ( methodRefPrintStringConsumer()) 中,对象是不同的

@Test
public void shouldNotSameFromFactoryMethod_methodRef() {
    Consumer<String> consumerA = methodRefPrintStringConsumer();
    Consumer<String> consumerB = methodRefPrintStringConsumer();

    Assert.assertNotSame(consumerA, consumerB);//consumerA == consumerB --> false
}
Run Code Online (Sandbox Code Playgroud)

直接方法返回与以下相同的结果shouldNotSameFromFactoryMethod_methodRef()

@SuppressWarnings("Convert2MethodRef")
@Test
public void shouldNotSameFromLambda() {
    Consumer<String> consumerA = s -> System.out.println(s);
    Consumer<String> consumerB = s -> System.out.println(s);

    Assert.assertNotSame(consumerA, consumerB);//consumerA == consumerB --> false
}
Run Code Online (Sandbox Code Playgroud)

,接下来我用方法引用其他静态方法测试了工厂方法

public class FunctionalInterfaceTest {

    public static Consumer<String> methodRefFromStaticMethodStringConsumer() {
        return FunctionalInterfaceTest::print;
    }

    public static void print(String string) {
        System.out.println(string);
    }

    ...

}
Run Code Online (Sandbox Code Playgroud)

并获得与第一个测试 ( lambdaPrintStringConsumer)相同的结果:

@Test
public void shouldSameFromFactoryMethod_methodRef() {
    Consumer<String> consumerA = methodRefFromStaticMethodStringConsumer();
    Consumer<String> consumerB = methodRefFromStaticMethodStringConsumer();

    Assert.assertSame(consumerA, consumerB );//consumerA == consumerB --> true
}
Run Code Online (Sandbox Code Playgroud)

诀窍是什么?

在测试 jdk-11.0.1jdk-13.0.1.

Tom*_*ine 5

以下表达式是否等价?

x -> System.out.println(x)

System.out::println
Run Code Online (Sandbox Code Playgroud)

不。如果你打电话 System.setOut,前者会接新的PrintStream;后者不会。

所以,在这种情况下,lambda 方法不需要从封闭的词法范围访问变量,而这个方法引用表达式需要。这允许前者共享,但后者不能。

可能会或可能不会指定确切的细节 - 我懒得看。

  • @kozmo,看起来你的问题与[this](/sf/ask/1961635511/)重复 (2认同)
  • @kozmo 要了解您问题的答案,您必须愿意了解先决条件。尤金给你的链接解释了这两种语言结构之间的根本区别(就像这个答案一样)。一旦您理解了这些差异,您就可以使用它们来回答您的问题,例如合并[“lambda 表达式每次执行时都会在堆上创建一个对象吗?”的答案](https://stackoverflow.com/a/ 27524543/2711488)。为了补充这个答案,两种构造都可以评估重用对象,但目前只有一个可以。 (2认同)