我是靠谱客的博主 害怕茉莉,最近开发中收集的这篇文章主要介绍Java - 为什么 Lambda 表达式要用 final 关键字修饰变量?,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

为什么 Lambda 表达式(匿名类) 不能访问非 final  的局部变量呢?

  • 结论
  1. Lambda 对于实例变量、静态变量不限制。
  2. 对于局部变量限制,必须是final类型,即使没有声明为final类型,后续这个变量也不可以被改变,如果是引用类型的属性的值,或者是 list 这种的增删可以,但重新赋值整个对象也不行。
  • 原因
  1. 局部变量有一个特点,存在于局部变量表中,属于线程私有,不共享。随着作用域的结束,可能会被内存回收。
  2. Lambda 是匿名内部类,如果和主线程运行时使用了不同的线程,那么很有可能在主线程结束后,局部变量已经销毁,或者发生了更改,那么就会导致实际使用和真实的是不一致的。
  3. 实例变量或者静态变量存放在堆中,线程共享,因此不受这个限制。

Java 8 的 Lambda 可以捕获什么变量呢?

(1)、捕获实例或静态变量是没有限制的 (可认为是通过 final 类型的局部变量 this 来引用前两者);

(2)、捕获的局部变量必须显式的声明为 final 或实际效果的的 final 类型。

回顾一下我们在 Java 8 之前,匿名类中如果要访问局部变量的话,那个局部变量必须显式的声明为  final。

Java 7 要求这个局部变量必须是 final  类型的,否则在匿名类中不可引用。

上面同样的代码放到 Java 8 中可以编译通过,难道 Java 8 不需要 version 是 final 的类型吗?不尽然

String version = "1.8";

foo(new Supplier() {

  @Override

  public String get() {

   return version;

  }

});

version = "1.7"; // 在 Java 8 下注释这行就能编译通过,否则报出前面同样的错误

也就是在 Java 8 下,即使局部变量未声明为 final 类型,一旦在匿名类中访问了一下就被强型加上了 final 属性,所以后面就无法再次给 version 赋值了。

前面演示了是匿名类,在 Java 8 中换成 Lambda 表达式也是一回事

String version = "1.8";

foo(() -> version); // 对局部变量 version 的访问让 version 变成 final 了

version = "1.7";  // 有了这行就编译不过了

因此,Java 8 的 Lambda 表达式访问局部变量时虽然没有硬性规定要被声明为 final,但实质上是和 Java 7 一样的。

总之一个局部变量如果要在 Java 7/8  的匿名类或是 Java 8 的 Lambda 表达式中访问,那么这个局部变量必须是 final 的。

注意,并不是 Lambda 开始访问时那个局部变量才变为 final,这是编译器的需求,例如

String version = "1.8";

version = "1.7";      // 注释掉这行或下行中另一行才能编译通过

foo(() -> version );  // 这行让编译器决定给 version 加上 final 属性

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

最后

以上就是害怕茉莉为你收集整理的Java - 为什么 Lambda 表达式要用 final 关键字修饰变量?的全部内容,希望文章能够帮你解决Java - 为什么 Lambda 表达式要用 final 关键字修饰变量?所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(77)

评论列表共有 0 条评论

立即
投稿
返回
顶部