s.get()
第二次返回“ONE”的解释是什么?
String x = "one";
Supplier<String> s = x::toUpperCase;
System.out.println("s.get() = " + s.get());
x = "two";
System.out.println("s.get() = " + s.get());
更新
将其与:
进行比较String x = "one";
Supplier<String> s = () -> x.toUpperCase();
System.out.println("s.get() = " + s.get());
x = "two";
System.out.println("s.get() = " + s.get());
它会抛出编译错误。
答案 0 :(得分:5)
在java变量中,引用对象通常称为references
。在上面的代码中,您有两个引用,x
和s
。
字符串是不可变的,任何更改都代表另一个Object。创建后,您无法修改String对象的任何状态。
在代码中,x
和s
都被启动以引用2个对象,然后x
引用另一个对象,但s
仍然引用同一个对象。请注意,会立即评估::
并生成对象。 x
可以将其对其他对象的引用更改为y
使用x = "two"
只会使x
引用其他对象。
答案 1 :(得分:3)
String是一个不可改变的类,你正在做
x = "two";
保持对象 s “完整”与之前的值“ONE”
答案 2 :(得分:3)
只有lambda表达式(the reasons why it works so)才能传递最终或有效的最终变量。使用方法引用,进行不同的评估,
15.13.3. Run-Time Evaluation of Method References
当方法引用表达式在
::
分隔符之前具有表达式(而不是类型)时,将立即计算该子表达式。存储评估结果,直到调用相应功能接口类型的方法为止;此时,结果将用作调用的目标引用。这意味着仅当程序遇到方法引用表达式时才会计算::
分隔符之前的表达式,并且不会在函数接口类型上的后续调用中重新评估。
所以变量不一定是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()
来电,都会重新评估。答案 3 :(得分: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();
}
}
答案 4 :(得分:0)
字符串是不可变的:
String x = "one";
Supplier<String> s = x::toUpperCase;
相当于:
String x = "one";
Supplier<String> s = "one"::toUpperCase;
答案 5 :(得分:-1)
您创建了一个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));