景象
在编写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.javajavap -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”可接管最新推送,外面也有我分享的一些优质资源。