前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >lambda表达式中使用的变量如何一定要有final修饰

lambda表达式中使用的变量如何一定要有final修饰

原创
作者头像
Blue_007
修改2023-11-07 07:49:04
4890
修改2023-11-07 07:49:04
举报
文章被收录于专栏:代码生涯

一、场景描述

在使用Java的多线程池,而在使用单线程线程池SingleThreadExecutor是出现了问题。

编译报错:Variable used in lambda expression should be final or effectively final

这句话的意思是,lambda 表达式中使用的变量应该是 final 或者有效的 final

二、解决方案

代码语言:java
复制
public static void main(String[] args) {
    // 创建一个单线程版的线程池
    ExecutorService es = Executors.newSingleThreadExecutor();
    // 使用
    for(int i=0;i<10;i++){
        int finalI = i;
        es.execute(() -> System.out.println(Thread.currentThread().getName()+"打印的值是"+ finalI));
    }
}

i这个变量赋值给了final变量,但是final并没有声明为final类型,然而代码却能够编译通过。

这是因为 Java8 之后,在匿名类或Lambda表达式中访问的局部变量,如果不是final类型的话,编译器自动加上final修饰符。

Java8 新特性:effectively final

三、原因

前面说 Lambda 表达式或者匿名内部类不能访问非 final 的局部变量,这是为什么呢?为什么会有这种规定?

3.1 首先思考外部的局部变量 finalI 和匿名内部类里面的 finalI 是否是同一个变量?

我们知道,每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接,方法出口等信息,每个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程(《深入理解Java虚拟机》第2.2.2节 Java虚拟机栈)

就是说在执行方法的时候,局部变量会保存在栈中,方法结束局部变量也会出栈,随后会被垃圾回收掉,而此时,内部类对象可能还存在,如果内部类对象这时直接去访问局部变量的话就会出问题,因为外部局部变量已经被回收了,解决办法就是把匿名内部类要访问的局部变量复制一份作为内部类对象的成员变量。

查阅资料或者通过反编译工具对代码进行反编译会发现,底层确实定义了一个新的变量,通过内部类构造函数将外部变量复制给内部类变量。


3.2 为何还需要用final修饰?

其实复制变量的方式会造成一个数据不一致的问题。

在执行方法的时候局部变量的值改变了却无法通知匿名内部类的变量,随着程序的运行,就会导致程序运行的结果与预期不同,于是使用final修饰这个变量,使它成为一个常量,这样就保证了数据的一致性。

我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、场景描述
  • 二、解决方案
  • 三、原因
    • 3.1 首先思考外部的局部变量 finalI 和匿名内部类里面的 finalI 是否是同一个变量?
      • 3.2 为何还需要用final修饰?
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档