原因
public class LambdaTest {
long l = 1L;
private void test() {
String s = "aaa";
Object localObj2 = new Object();
Integer a = 0;
Consumer consumer = (x) -> {
System.out.println(x);
System.out.println(l);
System.out.println(s);
System.out.println(a);
};
consumer.accept(localObj2);
}
}
之后编译该类,反编译字节码:
javac src/LambdaTest.java
javap -p src/LambdaTest.class
输出:
Compiled from "LambdaTest.java"
public class LambdaTest {
long l;
public LambdaTest();
private void test();
private void lambda$test$0(java.lang.String, java.lang.Integer, java.lang.Object);
}
相当于
long l = 1L;
private void test() {
String s = "aaa";
Object localObj2 = new Object();
Integer a = 0;
testlambda(localObj2,s,a);
}
private void testlambda(Object x,String s,Integer a){
System.out.println(x);
System.out.println(this.l);
System.out.println(s);
System.out.println(a);
}
由此可看出,Lambda表达式是一个语法糖,会被编译生成为当前类的一个私有方法,Lambda表达式内直接引用局部变量本质是一种隐式传参,编译时会自动将引用的局部变量放到参数列表中(Lambda方法多了个参数),而引用的实例变量并不需要放到参数列表,因为方法内可以直接引用。
所以在Lambda中对参数重新赋值或者在方法中将局部变量重新赋值,对另一方都是没有影响的。
因此,为了避免这种误导混淆,保证局部变量和Lambda的变量副本的数据一致性,Java直接在语法层面强制Lambda表达式引用的局部变量不可被重新赋值。