Java 将非final对象传递给方法引用
Java 将非final对象传递给方法引用,java,lambda,java-8,method-reference,expression-evaluation,Java,Lambda,Java 8,Method Reference,Expression Evaluation,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=“一”; 供应商s=x::toUpperCase; System.out.println(“s.get()=”+
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=“一”;
供应商s=x::toUpperCase;
System.out.println(“s.get()=”+s.get());
x=“两个”;
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());
String x=“一”;
供应商s=()->x.toUpperCase();
System.out.println(“s.get()=”+s.get());
x=“两个”;
System.out.println(“s.get()=”+s.get());
它将引发编译错误。字符串是一个不可变类,您正在执行
x = "two";
保持对象s与之前的值“ONE”保持“完整”您创建了一个
供应商,它只提供值,在这种情况下,每次都提供相同的值,因为创建lambda时,x
的值仅转换一次
您需要的是一个函数
,它接受一个参数并返回一个结果
试试这个:
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));
String x=“一”;
函数s=String::toUpperCase;
System.out.println(“s.apply(x)=”+s.apply(x));
x=“两个”;
System.out.println(“s.apply(x)=”+s.apply(x));
在java中,引用对象的变量通常称为引用
。在上面的代码中有两个引用,x
和s
字符串是不可变的,任何更改都表示另一个对象。一旦创建,就不能修改字符串对象的任何状态
在代码中,x
和s
都被初始化为引用2个对象,然后x
被设置为引用另一个对象,但是s
仍然引用同一个对象。请注意,:
将立即求值,并对结果对象进行分类x
可以独立于y
使用x=“two”
只能使x
引用不同的对象 只有lambda表达式()需要传递final或有效final变量。方法引用的评估方式不同
当方法引用表达式在分隔符前面有表达式(而不是类型)时,将立即计算该子表达式。存储评估结果,直到调用相应功能接口类型的方法;此时,结果将用作调用的目标引用。这意味着只有当程序遇到方法引用表达式时,才会对::
分隔符前面的表达式求值,并且不会在函数接口类型的后续调用中重新求值
因此,变量不必是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; }
}
A类{
公共静态void main(字符串[]args){
供应商1=A::新;//(1)
供应商2=新A()::self;//(2)
r1=supplier1.get();/(3)
r2=supplier2.get();/(4)
}
private A self(){返回此;}
}
已创建供应商实例,尚未评估结果(类型为的方法引用)
已计算供应商及其结果(使用newa()
表达式的方法引用)
对于每个supplier1.get()
调用,都将对其进行重新评估
将返回步骤2的结果
字符串是不可变的:
String x = "one";
Supplier<String> s = x::toUpperCase;
String x=“一”;
供应商s=x::toUpperCase;
相当于:
String x = "one";
Supplier<String> s = "one"::toUpperCase;
String x=“一”;
供应商s=“一”::toUpperCase;
这个问题很有趣,所以我通过反编译器运行了它-但答案支持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());
}
}
java-jar cfr_0_119.jar LambdaTest——decodelambdas false
/*
*使用CFR 0_119反编译。
*/
导入java.io.PrintStream;
导入java.lang.invoke.LambdaMetafactory;
导入java.util.function.Supplier;
公共类LambdaTest{
公共静态void main(字符串[]args){
字符串x=“一”;
suppliers=(Supplier)LambdaMetafactory.metafactory(null,null,null,()Ljava/lang/Object;,toUpperCase(),()Ljava/lang/String;)((String)x);
System.out.println(“s.get()=”+s.get());
x=“两个”;
System.out.println(“s.get()=”+s.get());
}
}
因此,方法引用获得了x的第一个实例的副本,这就是为什么它两次输出“1”,并且没有创建静态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();
}
}
java-jar cfr_0_119.jar LambdaTest——decodelambdas false
/*
*使用CFR 0_119反编译。
*/
导入java.io.PrintStream;
导入java.lang.invoke.LambdaMetafactory;
导入java.util.function.Supplier;
公共类LambdaTest{
公共静态void main(字符串[]args){
字符串y=“一”;
Supplier sy=(Supplier)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());
}
私有静态/*合成*/String lambda$0(字符串){
返回字符串.toUpperCase();
}
}
这比“简单的例子”更有趣,因为“引用是指引用,是一个合适的答案”。KelWoo:类似的,我可以说,你可以在C++中写类似于代码>供应商< /COD>的东西,输出“两个”。。第二个示例不应该因为有对实例的方法引用的原因而编译。好吧,该实例是一个文本为“one”的字符串。因此它永远不会更改,因为字符串是不可变的。您最好编写