logo头像
Snippet 博客主题

浅谈匿名函数,Lambda和闭包(Closure)

本文于742天之前发表,文中内容可能已经过时

几乎所有的主流编程语言都对函数式编程有支持,我所用过的比如Java8的Lambda表达式,JavaScript和Groovy语言的闭包(Closure)等,其他的类似于Object-C的block,python的Lambda和C++11,看到这些,我都有点眩晕想吐的感觉.

匿名函数,Lambda,闭包(Closure)区别

从表象上说Lambda和Closure是一个东西,只是语言不同罢了,它们都可以去替代匿名函数用于简化书写,匿名函数内部可以访问到外部变量.从而形成一个”闭包”.记住,这只是表象.下面就让你怀疑人生?真的是一样吗?

Java中Lambda表达式浅谈

其实Java在很早的版本就支持闭包,只是因为应用场景太少了,所以这个概念才从Java8推广.
话不多说,先上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static Supplier<Integer> testClosure(){

final int i = 1;

return new Supplier<Integer>() {

@Override

public Integer get() {

return i;

}

};

}

public interface Supplier<T> {

T get();
}

上述代码中,变量i是testClosure()的局部变量,但是最终返回里的匿名对象里,仍然返回了i.
我们知道,函数的局部变量,其作用域仅限于函数内部,在函数结束时,局部变量i从栈内存消失了,就应该是不可见状态,而闭包则将i的生存周期延长了,并且使得变量可以被外部函数所引用。
在JDK8之前,如果访问i,变量i必须加final关键字修饰,让i成为变为常量池中的常量以延迟i的生命周期.而在JDK8中以Lambda表达式方式写,i则不需要加,这里只是个语法糖,因为底层.class文件会自动推断加上.
在Java的经典著作《Effective Java》、《Java Concurrency in Practice》里,大神们都提到:匿名函数里的变量引用,也叫做变量引用泄露,会导致线程安全问题,因此在Java8之前,如果在匿名类内部引用函数局部变量,必须将其声明为final,即不可变对象。(Python和Javascript从一开始就是为单线程而生的语言,一般也不会考虑这样的问题,所以它的外部变量是可以任意修改的)。
如果用Java8的Lambda改写上述代码,则为:

1
2
3
4
5
6
7
8
9
10
11
public static Supplier<Integer> testClosure() {

int i = 1;

return () -> { //单个语句,可以省略{}

return i;

};

}

Wow,突然感觉改写后的代码简洁明了,高端大气上档次.

支付宝打赏 微信打赏

请作者喝杯咖啡吧