今天群里讨论javalambda实现.

后来不断衍生谈到了为什么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限制起来.

呜呼哀哉


参考资料:

https://stackoverflow.com/questions/4732544/why-are-only-final-variables-accessible-in-anonymous-class

Closure_(computer_programming)

lambda和匿名内部类使用外部变量为什么要语义final?的更多相关文章

  1. 为什么Java中lambda表达式不能改变外部变量的值,也不能定义自己的同名的本地变量呢?

    作者:blindpirate链接:https://www.zhihu.com/question/361639494/answer/948286842来源:知乎著作权归作者所有.商业转载请联系作者获得授 ...

  2. 关于Lambda表达式访问外部变量

    在<C#高级编程>一书中提到通过Lambda表达式可以访问Lambda表达式块外部的变量 ,这是一个很好的功能(类似Js中的 闭包).但是如果没有正确的使用,会非常危险. 比如下面的事例中 ...

  3. 匿名内部类中使用的外部局部变量为什么只能是final变量

    被匿名内部类引用的变量会被拷贝一份到内部类的环境中 但其后,在外部,该变量如果被修改,则内部外部不一致 Java为了避免数据不同步的问题,做出了匿名内部类只可以访问final的局部变量的限制. 究其原 ...

  4. python函数定义中引用外部变量的一个问题

    如果在函数定义的默认值中引用了一个外部变量,如下所示 x = 3 def func(a = x): print(a, x) 那么a的默认值就会是3, 但是print语句中的x会是调用时的x值 lamb ...

  5. 为什么内部类调用的外部变量必须是final修饰的?

    感谢原文:https://blog.csdn.net/u010393325/article/details/80643636 因为生命周期的原因.方法中的局部变量,方法结束后这个变量就要释放掉,fin ...

  6. PHP变量入门教程(4)PHP 的外部变量

    PHP 的外部变量 HTML 表单(GET 和 POST) 当一个表单体交给 PHP 脚本时,表单中的信息会自动在脚本中可用.有很多方法访问此信息,例如: 一个简单的 HTML 表单 <form ...

  7. 【C语言】C语言外部变量和内部变量

    目录: [外部变量] · 定义 · 用extern修饰变量 [内部变量] · 定义 · 用static修饰变量 1.外部变量 · 定义 定义的变量能被本文件和其它文件访问的变量,称为外部变量. 注: ...

  8. awk引用外部变量及调用系统命令方法

    目标:想用awk与scp命令批量传送文件 前提:先搭好主机间的免密登陆环境(参考:http://www.cnblogs.com/tankaixiong/p/4172942.html) 实现脚本方法: ...

  9. ajax 外部变量

    1.一般的js代码可以放在任何位置.但是用jquery写的代码需要先引入jquery文件,再写代码. 2.ajax函数中内部的变量不能传到外部.如果改变外部变量,需要async:false,代码如下: ...

随机推荐

  1. JavaScript设计模式之----组合模式

    javascript设计模式之组合模式 介绍 组合模式是一种专门为创建Web上的动态用户界面而量身制定的模式.使用这种模式可以用一条命令在多个对象上激发复杂的或递归的行为.这可以简化粘合性代码,使其更 ...

  2. 如何将网页保存为PDF文件

    怎样将网页保存为PDF文件... 问题: 很多时候我们需要将网页上的内容,在排版不变的情况下完整的保存下来,那么用pdf格式是最好的效果了,还图文并茂,效果与真实的网页很相似,如果另存为网页的话,会下 ...

  3. handler.go

    {         w.WriteHeader(http.StatusAccepted)     } else {         errStr := ""         for ...

  4. java.lang.IllegalArgumentException异常 数据库别名问题

    java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: Path expect ...

  5. BZOJ_2303_[Apio2011]方格染色 _并查集

    BZOJ_2303_[Apio2011]方格染色 _并查集 Description Sam和他的妹妹Sara有一个包含n × m个方格的 表格.她们想要将其的每个方格都染成红色或蓝色. 出于个人喜好, ...

  6. 循环table 示例

    1.循环table里的某一列 //当前第三列 //注:索引从1开始 $("#tabcardno tr td:nth-child(3)").each(function () { va ...

  7. java日志文件用法总结

    1.基本概念 日志门面(接口):commons logging ; slf4j ; 日志具体实现:log4j ; logback(Spring 系列在使用) ; log4j2(添加了异步logger的 ...

  8. python接口自动化(二十六)--批量执行用例 discover(详解)

    简介 我们在写用例的时候,单个脚本的用例好执行,那么多个脚本的时候,如何批量执行呢?这时候就需要用到 unittest 里面的 discover 方法来加载用例了.加载用例后,用 unittest 里 ...

  9. 基于pytorch的电影推荐系统

    本文介绍一个基于pytorch的电影推荐系统. 代码移植自https://github.com/chengstone/movie_recommender. 原作者用了tf1.0实现了这个基于movie ...

  10. 性能超前,详解腾讯云新一代Redis缓存数据库

    背景 当前内存数据库发展迅速,用户对于存储系统的要求也越来越高,为了满足各类业务场景的需要,腾讯云设计了新一代的内存数据库,不但保留了原来系统的高性能,高可用等特性,同时还兼容了当前流行的Redis原 ...