为什么lambda表达式要用final

探讨Java 8中Lambda表达式如何捕获外部变量,包括实例变量、静态变量及局部变量的规则,并解释为何Lambda不能访问非final的局部变量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自:http://unmi.cc/java-8-lambda-capture-outer-variables/

可能会把捕获外部变量的 Lambda 表达式称为闭包,那么 Java 8 的 Lambda 可以捕获什么变量呢?

  1. 捕获实例或静态变量是没有限制的(可认为是通过 final 类型的局部变量 this 来引用前两者)
  2. 捕获的局部变量必须显式的声明为 final 或实际效果的的 final 类型

回顾一下我们在 Java 8 之前,匿名类中如果要访问局部变量的话,那个局部变量必须显式的声明为  final,例如下面的代码放在 Java 7 中是编译不过的

java7-annymous-capture-local-variable

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

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

1
2
3
4
5
6
7
8
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 表达式也是一回事

1
2
3
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 的,即使没有  final 饰它也是  final 类型。

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

1
2
3
String version = "1.8";
version = "1.7";      //注释掉这行或下行中另一行才能编译通过
foo(() -> version );  //这行让编译器决定给 version 加上 final 属性

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

为什么 Lambda 表达式(匿名类) 不能访问非 final  的局部变量呢?因为实例变量存在堆中,而局部变量是在栈上分配,Lambda 表达(匿名类) 会在另一个线程中执行。如果在线程中要直接访问一个局部变量,可能线程执行时该局部变量已经被销毁了,而 final 类型的局部变量在 Lambda 表达式(匿名类) 中其实是局部变量的一个拷贝。

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值