为什么必须是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; 修饰引用数据类型变量 ...
随机推荐
- C++之字符串分割函数split
c++之字符串分割: /* *c++之字符串分割: */ #include <iostream> #include <string> #include <vector&g ...
- CentOS下配置多个Tomcat同时运行 本篇文章来源于 Linux公社网站(www.linuxidc.com)
原文地址:http://blog.csdn.net/tjcyjd/article/details/46553361 版权声明:本文为博主原创文章,未经博主允许不得转载. 同一服务器部署多个tomcat ...
- [Swust OJ 247]--皇帝的新衣(组合数+Lucas定理)
题目链接:http://acm.swust.edu.cn/problem/0247/ Time limit(ms): 1000 Memory limit(kb): 65535 Descriptio ...
- list,map的疑问
代码: package com.wyl; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator ...
- C# 网络编程 Part.1
本人也是新手,对网络编程一窍不通,所以从今天开始我将学习网络编程的基础知识,在此一一贴出来,编辑成一个系列! 1.为自己复习巩固用 2.可以找到同时在学习网络编程的同学,一起讨论交流,促进学习效率及其 ...
- POJ 1159 回文LCS滚动数组优化
详细解题报告可以看这个PPT 这题如果是直接开int 5000 * 5000 的空间肯定会MLE,优化方法是采用滚动数组. 原LCS转移方程 : dp[i][j] = dp[i - 1][j] + ...
- hdu 1026 Ignatius and the Princess I【优先队列+BFS】
链接: http://acm.hdu.edu.cn/showproblem.php?pid=1026 http://acm.hust.edu.cn/vjudge/contest/view.action ...
- muduo 与 libevent2 吞吐量对照
libevent 是一款很好用的 C 语言网络库,它也採用 Reactor 模型,正好能够与 muduo 做一对照. 本文用 ping pong 測试来对照 muduo 和 libevent2 的吞吐 ...
- SqlServer字段说明查询(表基本信息查询)
--快速查看表结构(比较全面的) THEN obj.name ELSE '' END AS 表名, col.colorder AS 序号 , col.name AS 列名 , ISNULL(ep.[v ...
- webform--常用的控件
一.简单控件 1.Lable——标签:在网页中呈现出来的时候会变成span标签 属性:Text——标签上的文字 BackColor,ForeColor——背景色,前景色 Font——字体 Bold- ...