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

Why Functional interface initialize different when use lambda in factory-method and method reference (singleton / prototype)?

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

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

public Consumer<String> methodRefPrintStringConsumer(){
    return System.out::println;
}

我发现在第一种情况下(lambdaPrintStringConsumer()),方法return引用了同一个对象

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

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

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

    Assert.assertNotSame(consumerA, consumerB);//consumerA == consumerB --> false
}

直接方法 return 结果与 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
}

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

public class FunctionalInterfaceTest {

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

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

    ...

}

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

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

    Assert.assertSame(consumerA, consumerB );//consumerA == consumerB --> true
}

什么是绝招

在测试中 jdk-11.0.1jdk-13.0.1

以下表达式是否等价?

x -> System.out.println(x)

System.out::println

没有。如果你调用System.setOut,前者会选择新的PrintStream;后者不会。

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

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