gongdear

gongdear的技术博客

欢迎大家参观我的博客
  menu
99 文章
89355 浏览
3 当前访客
ღゝ◡╹)ノ❤️

Lambda匿名内部类修改外部变量

正常在获取list中某个属性的总和时应该使用这种语法
//求所有交易员的年龄总和
BigDecimal allwages=transactions.stream()
.map(Transaction::getWages)
.reduce(new BigDecimal(0L),BigDecimal::add);

但是实际使用中有时候有特殊情况比如要在一个已有的变量上增加值
BigDecimal alld = new BigDecimal(0L);
transactions.stream().map(Transaction::getWages)
.forEach(
a-> alld = alld.add(a)
);
此时会报错
*Cannot assign a value to final variable
*Variable used in lambda expression should be final or effectively final
*无法改变final量的值
*不允许在Lambda表达式中修改使用的(外部)变量
由是观之,我们将Lambda的这种变量捕获行为称之为值捕获更加确切。

在实际操作中,如果我们在Lambda体内或匿名内部类中要对外部的某些量进行操作,那么这种限制也是很容易被规避,因为即使数组是final的(即该数组不能再指向其他数组对象),里面的值依旧可变。

所以我们只要将那个需要被操作的外部变量包装进一个数组即可:
final BigDecimal[] alld = {new BigDecimal(0L)};
transactions.stream().map(Transaction::getWages)
.forEach(
a-> alld[0] = alld[0].add(a)
);
此时就可以正常访问外部变量了
Java 8 的 Lambda 表达式访问局部变量时虽然没有硬性规定要被声明为 final,但实质上是和 Java 7 一样的。
一个局部变量如果要在 Java 7/8 的匿名类或是 Java 8 的 Lambda 表达式中访问,那么这个局部变量必须是 final 的,即使没有 final 饰它也是 final 类型。
换句话说,如果在匿名类或 Lambda 表达式中访问的局部变量,如果不是 final 类型的话,编译器自动加上 final 修饰符。
为什么 Lambda 表达式(匿名类) 不能访问非 final 的局部变量呢? 因为实例变量存在堆中,而局部变量是在栈上分配,Lambda 表达(匿名类) 会在另一个线程中执行。如果在线程中要直接访问一个局部变量,可能线程执行时该局部变量已经被销毁了,而 final 类型的局部变量在 Lambda 表达式(匿名类) 中其实是局部变量的一个拷贝。

宝剑锋从磨砺出,梅花香自苦寒来.