lambda和匿名内部类使用外部变量为什么要语义final?
今天群里讨论java的lambda实现.
后来不断衍生谈到了为什么lambda和匿名内部类只能使用语义final的外部变量.
最开始以为是java的lambda实现问题,编译期魔法会把外部引用作为参数传入所以在内部变化也影响不了下次调用的值,所以就干脆final了,如果用类的属性来保管这个变量就可以了.
In [64]: def outer(a:int):
...: def inner():
...: nonlocal a
...: a = a + a
...: return a
...: return inner
...:
In [65]: x = outer(1)
In [66]: x
Out[66]: <function __main__.outer.<locals>.inner>
In [67]: x()
Out[67]: 2
In [68]: x()
Out[68]: 4
In [69]: x()
Out[69]: 8
举例就是这种情况
lambda用参数传入外部int,如果在方法里修改了,下次调用这个lambda依旧是以前的值.
后来又去看了眼匿名内部类的实现
public class InnerTest {
public static void main(String[] args) {
String name = "123";
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println(name);
}
};
r.run();
}
}
InnerTest$1(java.lang.String val$name);
0 aload_0 [this]
1 aload_1 [val$name]
2 putfield InnerTest$1.val$name : java.lang.String [12]
5 aload_0 [this]
6 invokespecial java.lang.Object() [14]
9 return
Line numbers:
[pc: 0, line: 1]
[pc: 5, line: 11]
Local variable table:
[pc: 0, pc: 10] local: this index: 0 type: new InnerTest(){}
Method Parameters:
final synthetic val$name
构造方法字节码明明都存下来了呀...为什么那时候就要求final了
查阅了一下,发现这样有个很大的问题.
这个外部变量在匿名内部类初始化之后就被固定了下来,之后他如果被重新赋值(引用类型内部状态修改除外),就会出现内部无法看见外部,外部也无法看见内部的问题... ...
举个简单的例子:
In [101]: def outer(x):
...: def inner():
...: nonlocal x
...: x = x+1
...: print("inner"+str(x))
...: inner()
...: print("outer"+str(x))
...: x = x+1
...: inner()
...:
In [102]: outer(1)
inner2(inner内部+1)
outer2(外部看到这个变化)
inner4(外部+1 内部+1)
这段在py下能正常工作的代码,如果java没有final限制的话,就会变成
inner2(inner内部+1)
outer1(外部看不到inner变化)
inner3(inner内部+1)
所以这个约定和闭包实现没关系...
还是考虑在定义变量的作用域下规避掉因为java实现问题导致上面的结果返回...我既然没有nonlocal这样的机制(毕竟不支持引用传递....),索性就用final限制起来.
呜呼哀哉
参考资料:
Closure_(computer_programming)
lambda和匿名内部类使用外部变量为什么要语义final?的更多相关文章
- 为什么Java中lambda表达式不能改变外部变量的值,也不能定义自己的同名的本地变量呢?
作者:blindpirate链接:https://www.zhihu.com/question/361639494/answer/948286842来源:知乎著作权归作者所有.商业转载请联系作者获得授 ...
- 关于Lambda表达式访问外部变量
在<C#高级编程>一书中提到通过Lambda表达式可以访问Lambda表达式块外部的变量 ,这是一个很好的功能(类似Js中的 闭包).但是如果没有正确的使用,会非常危险. 比如下面的事例中 ...
- 匿名内部类中使用的外部局部变量为什么只能是final变量
被匿名内部类引用的变量会被拷贝一份到内部类的环境中 但其后,在外部,该变量如果被修改,则内部外部不一致 Java为了避免数据不同步的问题,做出了匿名内部类只可以访问final的局部变量的限制. 究其原 ...
- python函数定义中引用外部变量的一个问题
如果在函数定义的默认值中引用了一个外部变量,如下所示 x = 3 def func(a = x): print(a, x) 那么a的默认值就会是3, 但是print语句中的x会是调用时的x值 lamb ...
- 为什么内部类调用的外部变量必须是final修饰的?
感谢原文:https://blog.csdn.net/u010393325/article/details/80643636 因为生命周期的原因.方法中的局部变量,方法结束后这个变量就要释放掉,fin ...
- PHP变量入门教程(4)PHP 的外部变量
PHP 的外部变量 HTML 表单(GET 和 POST) 当一个表单体交给 PHP 脚本时,表单中的信息会自动在脚本中可用.有很多方法访问此信息,例如: 一个简单的 HTML 表单 <form ...
- 【C语言】C语言外部变量和内部变量
目录: [外部变量] · 定义 · 用extern修饰变量 [内部变量] · 定义 · 用static修饰变量 1.外部变量 · 定义 定义的变量能被本文件和其它文件访问的变量,称为外部变量. 注: ...
- awk引用外部变量及调用系统命令方法
目标:想用awk与scp命令批量传送文件 前提:先搭好主机间的免密登陆环境(参考:http://www.cnblogs.com/tankaixiong/p/4172942.html) 实现脚本方法: ...
- ajax 外部变量
1.一般的js代码可以放在任何位置.但是用jquery写的代码需要先引入jquery文件,再写代码. 2.ajax函数中内部的变量不能传到外部.如果改变外部变量,需要async:false,代码如下: ...
随机推荐
- BZOJ_1407_[Noi2002]Savage_EXGCD
BZOJ_1407_[Noi2002]Savage_EXGCD Description Input 第1行为一个整数N(1<=N<=15),即野人的数目. 第2行到第N+1每行为三个整数C ...
- BZOJ_4818_[Sdoi2017]序列计数_矩阵乘法
BZOJ_4818_[Sdoi2017]序列计数_矩阵乘法 Description Alice想要得到一个长度为n的序列,序列中的数都是不超过m的正整数,而且这n个数的和是p的倍数.Alice还希望 ...
- 利用FT232实现USB转串口
FT232B数据手册:http://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT232BL_BQ.pdf 常用的USB转串口的芯片有F ...
- Python + Appium 获取当前屏幕的截图方法的封装
使用方法:get_screenshot_as_file(filename),来自于selenium\webdriver\remote\webdiver.py def take_screenShot(s ...
- 关于EffictiveC++笔记
我根据自己的理解,对原文的精华部分进行了提炼,并在一些难以理解的地方加上了自己的"可能比较准确"的「翻译」.
- 浅谈surging服务引擎中的rabbitmq组件和容器化部署
1.前言 上个星期完成了surging 的0.9.0.1 更新工作,此版本通过nuget下载引擎组件,下载后,无需通过代码build集成,引擎会通过Sidecar模式自动扫描装配异构组件来构建服务引擎 ...
- JAVA线程及简单同步实现的原理解析
线程 一.内容简介: 本文主要讲述计算机中有关线程的相关内容,以及JAVA中关于线程的基础知识点,为以后的深入学习做铺垫.如果你已经是高手了,那么这篇文章并不适合你. 二.随笔正文: 1.计算机系统组 ...
- Haskell学习-monad
原文地址:Haskell学习-monad 什么是Monad Haskell是一门纯函数式的语言,纯函数的优点是安全可靠.函数输出完全取决于输入,不存在任何隐式依赖,它的存在如同数学公式般完美无缺.可是 ...
- sympy科学计算器
SymPy库常用函数 简介 本文抄于https://www.cnblogs.com/baby123/p/6296629.html SymPy是一个符号计算的Python库.它的目标是成为一个全功能的计 ...
- LeetCode二叉树的前序、中序、后序遍历(递归实现)
本文用递归算法实现二叉树的前序.中序和后序遍历,提供Java版的基本模板,在模板上稍作修改,即可解决LeetCode144. Binary Tree Preorder Traversal(二叉树前序遍 ...