这个问题在这里已有答案:
- 为什么方法引用可以使用非最终变量? 4个答案
起源
答案:
在java变量中,引用对象通常称为 references
。在上面的代码中,您有两个引用, x
和 s
。
字符串是不可变的,任何更改都代表另一个对象。创建后,您无法修改String对象的任何状态。
在代码中 x
和 s
被启动以引用2个对象然后 x
是为了引用另一个对象,但是 s
仍然指同一个对象。注意 ::
立即评估并生成对象。 x
可以独立于其他对象更改其引用 y
运用 x = "two"
只有 x
引用不同的对象。
String是一个不可改变的类,你正在做
x = "two";
保持对象 小号 “完整” 与之前的值“ONE”
仅通过lambda表达式传递最终或有效的最终变量(它起作用的原因)。使用方法引用,进行不同的评估,
当一个方法引用表达式时 有一个表达 前面的(而不是一种类型)
::
分隔符,立即计算子表达式。存储评估结果,直到调用相应功能接口类型的方法为止;此时,结果将用作调用的目标引用。这意味着前面的表达式::
只有当程序遇到方法引用表达式时,才会计算分隔符 在功能接口类型的后续调用中不会重新评估。
所以变量不必是a final
。
实际上,无论一个类是否是不可变的。相反,如果方法引用的左侧部分是表达式,则很重要。
我想举一个简短的例子让你明白:
class A {
public static void main(String[] args) {
Supplier<A> supplier1 = A::new; // (1)
Supplier<A> supplier2 = new A()::self; // (2)
A r1 = supplier1.get(); // (3)
A r2 = supplier2.get(); // (4)
}
private A self() { return this; }
}
- 已创建供应商实例,结果尚未评估(带有类型的方法引用)。
- 计算供应商及其结果(方法参考用
new A()
表达)。 - 对于每一个
supplier1.get()
打电话,它将被重新评估。 - 将返回步骤2的结果。
有趣的问题,所以我通过反编译器运行它 - 但答案支持Andrew Tobilko回答
java -jar cfr_0_119.jar LambdaTest --decodelambdas false
/*
* Decompiled with CFR 0_119.
*/
import java.io.PrintStream;
import java.lang.invoke.LambdaMetafactory;
import java.util.function.Supplier;
public class LambdaTest {
public static void main(String[] args) {
String x = "one";
Supplier<String> s = (Supplier<String>)LambdaMetafactory.metafactory(null, null, null, ()Ljava/lang/Object;, toUpperCase(), ()Ljava/lang/String;)((String)x);
System.out.println("s.get() = " + s.get());
x = "two";
System.out.println("s.get() = " + s.get());
}
}
所以方法引用是获取x的第一个实例的副本,这就是为什么它输出“ONE”两次,并且没有创建静态lambda,只调用toUpper
我还运行了第二个例子,它创建了一个lambda(我错过了不编译的部分 -
java -jar cfr_0_119.jar LambdaTest --decodelambdas false
/*
* Decompiled with CFR 0_119.
*/
import java.io.PrintStream;
import java.lang.invoke.LambdaMetafactory;
import java.util.function.Supplier;
public class LambdaTest {
public static void main(String[] args) {
String y = "one";
Supplier<String> sy = (Supplier<String>)LambdaMetafactory.metafactory(null, null, null, ()Ljava/lang/Object;, lambda$0(java.lang.String ), ()Ljava/lang/String;)((String)y);
System.out.println("sy.get() = " + sy.get());
}
private static /* synthetic */ String lambda$0(String string) {
return string.toUpperCase();
}
}
字符串是不可变的:
String x = "one";
Supplier<String> s = x::toUpperCase;
相当于:
String x = "one";
Supplier<String> s = "one"::toUpperCase;
你创建了一个 Supplier
,它只提供值,在这种情况下每次都是相同的值,因为值 x
这里只在创建lambda时转换一次。
你想要的是一个 Function
,接受参数并返回结果的东西。
尝试这个:
String x = "one";
Function<String, String> s = String::toUpperCase;
System.out.println("s.apply(x) = " + s.apply(x));
x = "two";
System.out.println("s.apply(x) = " + s.apply(x));