为什么在工厂方法和方法引用(单例/原型)中使用 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.1
和 jdk-13.0.1
。
以下表达式是否等价?
x -> System.out.println(x)
System.out::println
没有。如果你调用System.setOut
,前者会选择新的PrintStream
;后者不会。
因此,在这种情况下,lambda 方法不需要访问封闭词法范围内的变量,而此方法引用表达式需要。这允许前者共享但后者不能。
可能会或可能不会指定确切的详细信息 - 我懒得看。
我有两个生产“消费者”的工厂方法使用不同的方法 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.1
和 jdk-13.0.1
。
以下表达式是否等价?
x -> System.out.println(x)
System.out::println
没有。如果你调用System.setOut
,前者会选择新的PrintStream
;后者不会。
因此,在这种情况下,lambda 方法不需要访问封闭词法范围内的变量,而此方法引用表达式需要。这允许前者共享但后者不能。
可能会或可能不会指定确切的详细信息 - 我懒得看。