乐趣区

关于java:为什么Java-Lambda表达式引用的局部变量需要final修饰

景象

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

退出移动版