Java 中的闭包之争
来源:互联网 发布:陌陌直播软件 编辑:程序博客网 时间:2024/05/16 17:15
闭包一直都是
Java
社区中争论不断的话题,很多语言例如JavaScript
,Ruby
,Python
等都支持闭包这个语言特性,闭包功能强大且灵活,Java
并没有显式地支持它,但其实Java
中也存在着所谓的”闭包”.
本文作者为: SylvanasSun.转载请务必将下面这段话置于文章开头处(保留超链接).
本文转发自SylvanasSun Blog,原文链接: https://sylvanassun.github.io/2017/07/30/2017-07-30-JavaClosure/
闭包
定义一个闭包的要点如下:
- 一个依赖于外部环境的
自由变量
的函数.
- 这个函数能够访问外部环境的
自由变量
.
也就是说,外部环境持有内部函数所依赖的自由变量
,由此对内部函数形成了闭包.
自由变量
那么什么是自由变量
呢?自由变量
就是在函数自身作用域之外的变量,一个函数y
就是自由变量
,它并不是这个函数自身的自变量,而是通过外部环境提供的.
下面以JavaScript
的一个闭包为例:
12345
function Add(y) {return function(x) {return x + y;}}
对于内部函数function(x)
来说,y
就是自由变量
.而y
是函数Add(y)
内的参数,所以Add(y)
对内部函数function(x)
形成了一个闭包.
这个闭包将自由变量y
与内部函数绑定在了一起,也就是说,当Add(y)
函数执行完毕后,它不会随着函数调用结束后被回收(不能在栈上分配空间).
12
var add_function = Add(5); // 这时y=5,并且与返回的内部函数绑定在了一起var result = add_function(10); // x=10,返回最终的结果 10 + 5 = 15
Java中的闭包
Java
与JavaScript
又或者其他支持闭包的语言不同,它是一个基于类的面向对象语言,也就是说一个方法所用到的自由变量
永远都来自于其所在类的实例的.
1234567
class AddUtils {private int y = 5;public int add(int x) {retrun x + y;}}
这样一个方法add(x)
拥有一个参数x
与一个自由变量y
,它的返回值也依赖于这个自由变量y
.add(x)
想要正常工作的话,就必须依赖于AddUtils
类的一个实例,不然它无法知道自由变量y
的值是多少,也就是自由变量
未与add(x)
进行绑定.
严格上来说,add(x)
中的自由变量
应该为this
,这是因为y
也是通过this
关键字来访问的.
所以说,在Java
中闭包其实无处不在,只不过我们难以发现而已.但面向对象的语言一般都不把类叫成闭包,这是一种习惯.
Java
中的内部类就是一种典型的闭包结构.
123456789101112
public class Outer {private int y = 5;private class Inner {private int x = 10;public int add() {return x + y;}}}
内部类通过一个指向外部类的引用来访问外部环境中的自由变量
,由此形成了一个闭包.
匿名内部类
12345678910111213141516
public interface AnonInner() {int add();}public class Outer {public AnonInner getAnonInner(final int x) {final int y = 5;return new AnonInner() {public int add() {return x + y;}}}}
getAnonInner(x)
方法返回了一个匿名内部类AnonInner
,匿名内部类不能显式地声明构造函数,也不能对构造函数传参,且返回的是一个AnonInner
接口,但它的add()
方法实现中用到了两个自由变量
(x
与y
),也就是说外部方法getAnonInner(x)
对这个匿名内部类构成了闭包.
但我们发现自由变量
都被加上了final
修饰符,这是因为Java
对闭包支持的不完整导致的.
对于自由变量
的捕获策略有以下两种:
- capture-by-value: 只需要在创建闭包的地方把捕获的值拷贝一份到对象里即可.
Java
的匿名内部类和Java 8
新的lambda
表达式都是这样实现的.
- capture-by-reference: 把被捕获的局部变量“提升”(hoist)到对象里.
C#
的匿名函数(匿名委托/lambda表达式)就是这样实现的.
Java
只实现了capture-by-value
,但又没有对外说明这一点,为了以后能进一步扩展成支持capture-by-reference
留后路,所以干脆就不允许向被捕获的变量赋值,所以这些自由变量
需要强制加上final
修饰符(在Jdk8
中似乎已经没有这种强制限制了).
参考文献
- Java theory and practice: The closures debate
- 关于对象与闭包的关系的一个有趣小故事
- JVM的规范中允许编程语言语义中创建闭包(closure)吗? - 知乎
- 为什么Java闭包不能通过返回值之外的方式向外传递值? - 知乎
- Java 中的闭包之争
- Java中的闭包之实例一
- JAVA中的闭包
- java中的闭包
- java中的闭包
- Java学习笔记之Java中的包
- 闭包(Java中的闭包)
- Java闭包之我见
- JavaScript中的闭包之我见
- JavaScript中的闭包之彻底领悟
- js中的闭包之我理解
- js中的闭包之我理解
- js中的闭包之我理解
- js中的闭包之我理解
- js中的闭包之我理解
- js中的闭包之我理解
- js中的闭包之我理解
- java中的闭包与回调
- python装饰器
- 【备忘】最新spark/hadoop/hbase/hive/kafka/redies大数据视频教程
- wifi p2p学习
- C/C++面试常见问题总结
- select2应用在modal模态框里,select2搜索框不能输入值
- Java 中的闭包之争
- SAP收货后更改采购单价格
- Android7.0 PowerManagerService(3) 核心函数updatePowerStateLocked的主要流程
- Linux命令(19)——tar命令
- POJ
- window7 Qt5.9.2 编译 QtAV QMLPlayer 播放器
- 自己的经历(1)dubbo的初识
- Number Sequence
- linux下获取占用CPU资源最多的10个进程,可以使用如下命令组合: ps aux|head -1;ps aux|grep -v PID|sort -rn -k +3|head linux下