Variable used in lambda expression should be final or effectively final问题理解

1、问题产生的背景

先在方法内部初始化一个Map,然后不同的条件下通过其它服务接口返回一个Map赋给这个Map,最后在lambda表达式中使用这个Map获取值,结果报编译错误 Variable used in lambda expression should be final or effectively final。
直接上代码
在这里插入图片描述

2、解决

不初始化Map,如图
在这里插入图片描述

3、为什么?

lambda表达式只能调用局部变量,却不能改变其值。因此,只要进入方法中的lambda表达式,就会被认为是final类型。但是修改引用类型里面的属性值是没有问题,因为本质上变量的地址还是没变。
由此可见,如果直接给这个Map加上final修饰,那么调用其它服务将结果赋值给这个Map是行不通,这等于是改变了引用地址。如果不初始化,只是定义出来,那么在赋值时才初始化,后续没了重新赋值的操作,虽不符合final要求,但符合effectively final(变量没有更新,引用对象引用地址没有更新)要求。

4、为什么lambda表达式中使用的局部变量需要是final或effectively final呢?

原因可能有:
1、局部内部类**(lambda表达式等同于匿名内部类,在方法中又是局部的)**的生命周期肯定是比局部变量的生命周期要长。因为局部变量在方法结束后就没了,但是局部内部类和其他的类一样,要所有的引用都没有了才会被回收,那么如果方法结束后这个匿名内部类还没回收的话,就肯定不行的(内部类要用的局部变量随着方法结束被回收了),就会报错。那么加final就行了吗?对的,如果一个 final 变量被封装到一个匿名内部类、Lambda 表达式或方法引用中,该变量会被隐式地转换为一个成员变量,并且其生命周期将是整个匿名对象的生命周期,等于说它的生命周期被延长了
2、一个方法对应一个栈帧,lambda表达式中的逻辑也是方法,这就有两个栈帧,栈帧之间是不能访问各自的局部变量的,所以局部变量在传递到内部类时,是复制一份到自己的构造函数中。那么如果局部变量在外面被修改了,就会造成内外不同步问题,lambda为避免这个问题,就直接禁止修改,因此需要final修饰,或者知道没有改动(effectively final)。

5、为什么成员变量不用final就可以访问了?

同上面两个原因:
1、生命周期角度:成员变量是和这个类的对象挂钩的,这个匿名内部类没有被回收,这个对象肯定就不会被回收,那么这个成员变量就肯定存在。就不用担心生命周期的问题
2、数据同步角度:成员变量是存在在堆中的,堆是线程共享的,那么在lambda对应的栈中,就可以直接访问到这个成员变量,数据被修改后lambda表达式就一定能同步到,不会产生脏数据问题。

补充个更直观的测试代码:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr Yin

您获益,我得意,您打赏,我敬礼

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值