java lambda 表达式的局部变量为什么必须是final修饰?

以下内容是转载至两位大佬的:

结论

  1. lambda对于实例变量、静态变量不限制。
  2. 对于局部变量限制,必须是final类型, 即使没有声明为final类型, 后续这个变量也不可以被改变, 如果是引用类型的属性的值,或者是list这种的增删可以,但重新赋值整个对象也不行.

原因

  1. 局部变量有一个特点, 存在于局部变量表中, 属于线程私有, 不共享. 随着作用域的结束, 可能会被内存回收。
  2. lambda是匿名内部类, 如果和主线程运行时使用了不同的线程, 那么很有可能在主线程结束后, 局部变量已经销毁, 或者发生了更改, 那么就会导致实际使用和真实的是不一致的。
  3. 我理解正因为如此, 实例变量或者静态变量存放在堆中, 线程共享, 因此不受这个限制.

原文链接:https://blog.csdn.net/zhangjingao/article/details/122732331

—————————————————————————————————————————————

现象
在这里插入图片描述

在编写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表达式引用的局部变量不可被重新赋值。

原文链接:https://blog.csdn.net/qq_40419564/article/details/112569172

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值