为什么必须是final的呢?
一个谜团
如果你用过类似guava这种“伪函数式编程”风格的library的话,那下面这种风格的代码对你来说应该不陌生:
1 |
|
这段代码对一个字符串的list进行过滤,从中找出长度为4的字符串。看起来很是平常,没什么特别的。
但是,声明expectedLength时用的那个final看起来有点扎眼,把它去掉试试:
error: local variable expectedLength is accessed from within inner class; needs to be declared final
结果Java编译器给出了如上的错误,看起来匿名内部类只能够访问final的局部变量。但是,为什么呢?其他的语言也有类似的规定吗?
在开始用其他语言做实验之前我们先把问题简化一下,不要再带着guava了,我们去除掉噪音,把问题归结为:
为什么Java中的匿名内部类只可以访问final的局部变量呢?其他语言中的匿名函数也有类似的限制吗?
Scala中有类似的规定吗?
1 |
|
上面的Scala代码是合法的,number变量是声明为var的,不是val(类似于Java中的final)。而且在匿名函数中可以修改number的值。
看来Scala中没有类似的规定。
C#中有类似的规定吗?
1 |
|
这段C#代码也是合法的,number这个局部变量在lambda表达式内外都可以访问和赋值。
看来C#中也没有类似的规定。
分析谜团
三门语言中只有Java有这种限制,那我们分析一下吧。先来看一下Java中的匿名内部类是如何实现的:
先定义一个接口:
1 |
|
然后创建这个接口的匿名子类:
1 |
|
这个匿名子类会被编译成一个单独的类,反编译的结果是这样的:
1 |
|
可以看到名为number的局部变量是作为构造方法的参数传入匿名内部类的(以上代码经过了手动修改,真实的反编译结果中有一些不可读的命名)。
如果Java允许匿名内部类访问非final的局部变量的话,那我们就可以在TryUsingAnonymousClass$1中修改paramInteger,但是这不会对number的值有影响,因为它们是不同的reference。
这就会造成数据不同步的问题。
所以,谜团解开了:Java为了避免数据不同步的问题,做出了匿名内部类只可以访问final的局部变量的限制。
但是,新的谜团又出现了:
Scala和C#为什么没有类似的限制呢?它们是如何处理数据同步问题的呢?
上面出现过的那段Scala代码中的lambda表达式会编译成这样:
1 |
|
可以看到number也是通过构造方法的参数传入的,但是与Java的不同是这里的number不是直接传入的,是被IntRef包装了一层然后才传入的。对number的值修改也是通过包装类进行的:this.number$2.elem = 456;
这样就保证了lambda表达式内外访问到的是同一个对象。
再来看看C#的处理方式,反编译一下,发现C#编译器生成了如下的一个类:
1 |
|
把number包装在这个类内,这样就保证了lambda表达式内外使用的都是同一个number,即便重新赋值也可以保证内外部的数据是同步的。
小结
Scala和C#的编译器通过把局部变量包装在另一个对象中,来实现lambda表达式内外的数据同步。
而Java的编译器由于未知的原因(怀疑是为了图省事儿?)没有做包装局部变量这件事儿,于是就只好强制用户把局部变量声明为final才能在匿名内部类中使用来避免数据不同步的问题。
为什么必须是final的呢?的更多相关文章
- java抽象、接口 和final
抽象 一.抽象类:不知道是具体什么东西的类. abstract class 类名 1.抽象类不能直接new出来. 2.抽象类可以没有抽象方法. public abstract class USB { ...
- Java内部类final语义实现
本文描述在java内部类中,经常会引用外部类的变量信息.但是这些变量信息是如何传递给内部类的,在表面上并没有相应的线索.本文从字节码层描述在内部类中是如何实现这些语义的. 本地临时变量 基本类型 fi ...
- Java关键字final、static
一.final根据程序上下文环境,Java关键字final有“这是无法改变的”或者“终态的”含义,它可以修饰非抽象类.非抽象类成员方法和变量.你可能出于两种理解而需要阻止改变:设计或效率. final ...
- JavaSE 之 final 初探
我们先看一道面试题: 请问 final 的含义是什么?可以用在哪里?其初始化的方式有哪些? 首先我们回答一下这道题,然后再探究其所以然. 1.final 表示“最终的”.“不可改变的”,意指其修饰类 ...
- PHP的final关键字、static关键字、const关键字
在PHP5中新增加了final关键字,它可以加载类或类中方法前.但不能使用final标识成员属性,虽然final有常量的意思,但在php中定义常量是使用define()函数来完成的. final关键字 ...
- final修饰符
final本身的含义是"最终的,不可变的",它可以修饰非抽象类,非抽象方法和变量.注意:构造方法不能使用final修饰,因为构造方法不能被继承,肯定是最终的. final修饰的类: ...
- 浅析Java中的final关键字(转载)
自http://www.cnblogs.com/dolphin0520/p/3736238.html转载 一.final关键字的基本用法 在Java中,final关键字可以用来修饰类.方法和变量(包括 ...
- java关键字extends(继承)、Supe(父类引用空间)、 This(方法调用者对象)、Instanceof(实例类型-判断对象是否属于某个类)、final(最终)、abstract(抽象) 、interface(接口)0
java 继承使用关键字extends 继承的作用:减少代码量,优化代码 继承的使用注意点: 1子类不能继承父类的私有变量 2.子类不能继承父类的构造方法 3.子类在调用自己的构造方法时 会默认调 ...
- files list file for package 'xxx' is missing final newline
#!/usr/bin/python # 8th November, 2009 # update manager failed, giving me the error: # 'files list f ...
- final关键字(final是最终的)
final关键字(final是最终的) 1.final修饰特点 a.修饰类,类不能被继承 b.修饰变量,变量就变成了常量, 修饰基本数据类:final int num = 10; 修饰引用数据类型变量 ...
随机推荐
- Cocoapods安装步骤
Cocoapods安装步骤 1.升级Ruby环境 终端输⼊入:$ gem update --system 此时会出现 ERROR: While executing gem ... (Gem::File ...
- [Swust 549]--变位词(vector水过)
Time limit(ms): 1000 Memory limit(kb): 65535 Description 输入N和一个要查找的字符串,以下有N个字符串,我们需要找出其中的所有待查找字符串的 ...
- 7.2.1 生成1~n的排列(全排列)【STL__next_permutation()_的应用】
#include <stdio.h> #include <math.h> #include <string.h> #include <stdlib.h> ...
- Oracle 专用模式(DEDICATED) 和 共享模式(SHARE) (转)
Oracle 是一门博大精深的技术.玩了2年的oracle,依旧还有很多知识点不清楚. 昨天群里的朋友提到了 DEDICATED 和 SHARE 两种模式. 不清楚,默默的做点功课了.从网上搜了点知识 ...
- 0-1背包的动态规划算法,部分背包的贪心算法和DP算法------算法导论
一.问题描述 0-1背包问题,部分背包问题.分别实现0-1背包的DP算法,部分背包的贪心算法和DP算法. 二.算法原理 (1)0-1背包的DP算法 0-1背包问题:有n件物品和一个容量为W的背包.第i ...
- host字段变复杂了
声明: 本博客欢迎转载,但请保留原作者信息! 作者:李人可 团队:华为杭州OpenStack团队 讨论的是openstack中卷的host属性. 印象中.社区H版本号对于volume的host值表示的 ...
- Linux内核源代码解析之——我与神童聊Linux内核
本文原创为freas_1990,转载请标明出处:http://blog.csdn.net/freas_1990/article/details/11619609 我的朋友里,至少有2.5个神童. 有的 ...
- hdu1853解题报告
题意和解决回路匹配的思路如同hdu3488 (这里我第一次想到最短路,但是对于有回路这个不知道怎么处理,后来看了别人的解题报告才知道KM匹配,但是看到KM之后就自己想...想了很久....还是不知道回 ...
- 【原创】Android 系统稳定性 - ANR(二)
文章都为原创,转载请注明出处,未经允许而盗用者追究法律责任. 很久之前写的了,留着有点浪费,共享之.编写者:李文栋P.S. OpenOffice粘贴过来后格式有些混乱. 1.2 如何分析ANR问题 引 ...
- 转载python并行运算实例
Python的并发处理能力臭名昭著.先撇开线程以及GIL方面的问题不说,我觉得多线程问题的根源不在技术上而在于理念.大部分关于Pyhon线程和多进程的资料虽然都很不错,但却过于细节.这些资料讲的都是虎 ...