景象
在编写 Lambda 表达式的时候,有时会援用 Lambda 表达式以外的变量。
Lambda 表达式援用的 局部变量 必须是 最终变量 或实际上的最终变量 ,也就是说局部变量在被创立后不得被 从新赋值。
剖析
那么为什么呢?我看了网上的许多文章,写的都比拟含糊,例如:
它所能拜访到的外部类中的各种变量都只是 一份拷贝,因而为了避免你误以为可能间接批改外部类的变量,做了这样的设定。
我看的云里雾里,直到我看了这篇文章,【小家 java】应用 lambda 表达式传参是否有性能问题?,外面分析了 Lambda 表达式这个语法糖的原理,之后我写了一个 Test 去验证:
import java.util.function.Consumer;
public class LambdaTest {Object instanceObj = new Object();
private void test() {
// 用于间接援用
Object localObj1 = new Object();
// 用于传参
Object localObj2 = new Object();
Consumer consumer = (x) -> {System.out.println(x);
System.out.println(localObj1);
System.out.println(instanceObj);
};
consumer.accept(localObj2);
}
}
之后编译该类,反编译字节码:
javac src/LambdaTest.java
javap -p src/LambdaTest.class
输入:Compiled from "LambdaTest.java"
public class LambdaTest {
java.lang.Object instanceObj;
public LambdaTest();
private void test();
private void lambda$test$0(java.lang.Object, java.lang.Object);
}
由此可看出,Lambda 表达式是一个 语法糖 ,会被编译生成为以后类的一个 公有办法 ,Lambda 表达式内间接援用局部变量实质是一种 隐式传参 , 编译时会主动将援用的局部变量放到参数列表中 (Lambda 办法多了个参数),而援用的 实例变量并不需要放到参数列表,因为办法内能够间接援用。
那么造成 间接援用的局部变量 须要 final 润饰的起因应该和这种 隐式传参 无关,所以这里须要再提一下 Java 办法的 传参机制,详见之前发过的 Java 对象空间占用如何计算?
Java 中援用数据类型是由 援用变量和 指向的理论对象 两局部组成的。在办法传参时,实质上是将理论对象的 内存地址 赋值给办法参数中的 援用变量 **
所以在 Lambda 中 对参数从新赋值 或者在办法中 将局部变量从新赋值 ,对另一方都是 没有影响 的。
论断
因而,为了防止这种误导混同,保障局部变量和 Lambda 的变量正本的 数据一致性 ,Java 间接在语法层面强制 Lambda 表达式援用的局部变量 不可被从新赋值。
以上分析仅是我作为一个学习者的集体了解,如有谬误欢送留言进行斧正,咱们共同进步,谢谢!
感谢您浏览本文,关注我的公众号“语冰 Yubing”可接管最新推送,外面也有我分享的一些优质资源。