文档章节

lambda表达式和闭包

黄亿华
 黄亿华
发布于 2013/09/15 05:57
字数 940
阅读 4746
收藏 38

区分lambda表达式和闭包

熟悉的Javascript或者Ruby的同学,可能对另一个名词:闭包更加熟悉。因为一般闭包的示例代码,长得跟lambda差不多,导致我也在以前很长一段时间对这两个概念傻傻分不清楚。其实呢,这两个概念是完全不同维度的东西。

闭包是个什么东西呢?我觉得Ruby之父松本行弘在《代码的未来》一书中解释的最好:闭包就是把函数以及变量包起来,使得变量的生存周期延长。闭包跟面向对象是一棵树上的两条枝,实现的功能是等价的。

这样说可能不够直观,我们还是用代码说话吧。其实Java在很早的版本就支持闭包了,只是因为应用场景太少,这个概念一直没得到推广。在Java6里,我们可以这样写:

<!-- lang: java -->
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的生存周期延长了,并且使得变量可以被外部函数所引用。这就是闭包了。这里,其实我们的lambda表达式还没有出现呢!

而支持lambda表达式的语言,一般也会附带着支持闭包了,因为lambda总归在函数内部,与函数局部变量属于同一语句块,如果不让它引用局部变量,不会让人很别扭么?例如Python的lambda定义我觉得是最符合λ算子的形式的,我们可以这样定义lambda:

<!-- lang: python -->
#!/usr/bin/python
y = 1
f=lambda x: x + y
print f(2)
y = 3
print f(2)
输出:	
3
5

这里y其实是外部变量。

Java中闭包带来的问题

在Java的经典著作《Effective Java》、《Java Concurrency in Practice》里,大神们都提到:匿名函数里的变量引用,也叫做变量引用泄露,会导致线程安全问题,因此在Java8之前,如果在匿名类内部引用函数局部变量,必须将其声明为final,即不可变对象。(Python和Javascript从一开始就是为单线程而生的语言,一般也不会考虑这样的问题,所以它的外部变量是可以任意修改的)。

在Java8里,有了一些改动,现在我们可以这样写lambda或者匿名类了:

<!-- lang: java -->
public static Supplier<Integer> testClosure() {
    int i = 1;
    return () -> {
        return i;
    };
}

这里我们不用写final了!但是,Java大神们说的引用泄露怎么办呢?其实呢,本质没有变,只是Java8这里加了一个语法糖:**在lambda表达式以及匿名类内部,如果引用某局部变量,则直接将其视为final。**我们直接看一段代码吧:

<!-- lang: java -->
public static Supplier<Integer> testClosure() {
    int i = 1;
    i++;
    return () -> {
        return i; //这里会出现编译错误
    };
}

明白了么?其实这里我们仅仅是省去了变量的final定义,这里i会强制被理解成final类型。很搞笑的是编译错误出现在lambda表达式内部引用i的地方,而不是改变变量值的i++…这也是Java的lambda的一个被人诟病的地方。我只能说,强制闭包里变量必须为final,出于严谨性我还可以接受,但是这个语法糖有点酸酸的感觉,还不如强制写final呢…

© 著作权归作者所有

共有 人打赏支持
黄亿华

黄亿华

粉丝 2372
博文 131
码字总数 116344
作品 7
程序员
私信 提问
加载中

评论(2)

哈库纳
哈库纳
一直期待 这个功能。
haanya168
haanya168
是啊 如果为了省去一些代码 却使编译错误提示定位不准确 还不如不省去呢!
使用闭包捕获状态

Java 8 习惯用语,第 10 部分 使用闭包捕获状态 Lambda 表达式是无状态的,但您的程序不必如此 Venkat Subramaniam 2017 年 1 月 03 日发布 系列内容: 此内容是该系列 10 部分中的第 # 部分...

Venkat Subramaniam
2017/01/03
0
0
JavaSE_8系列博客——Java8的新特性(一)--Lambda表达式(1)--宏观把控

Why Lambda 表达式? 解决匿名内部类的垂直问题: 匿名内部类最大的痛楚就是笨重(冗繁、可读性差)。我们可以称之为“垂直问题” Lambda表达式是匿名方法,旨在使用轻量级机制代替匿名内部类...

u011500356
2017/09/30
0
0
[Kotlin] 关于lambda,你想知道的都在这里

从Java语言转到Kotlin,最让人头疼的问题恐怕就是lambda表达式了。 lambda,准确的中文翻译是:匿名函数。 不过,在Kotlin语言中本身就有匿名函数的概念,为了区分,我们姑且把它叫做Lambda表...

欧阳锋
2017/11/26
0
0
[iOS、Unity、Android] 浅谈闭包的使用方法

前言 我们经常所编程语言的的进步速度是落后于硬件的发展速度的。 但是最近几年,闭包语法在各个语言中都有自己的体现形式,例如   •  C语言中使用函数指针作为回调函数的入口;   •...

浩浩老师
2015/09/07
43
0
CUDA学习(七十七)

扩展的Lambda类型特征: 编译器提供类型特征来在编译时检测扩展lambda表达式的闭包类型(closure types): :如果'type'是为扩展的创建的闭包类,那么该特征为true,否则为false。 :如果'...

night李
2018/02/24
0
0

没有更多内容

加载失败,请刷新页面

加载更多

比特币转账nodejs

去nodejs官网下载nodejs $ node index.js启动项目

八戒八戒八戒
25分钟前
1
0
Android8.1 SystemUI 之图案锁验证流程

在Keyguard之滑动解锁流程一文中,我们已经分析过,不同的安全锁类型是在KeyguardSecurityContainer中使用getSecurityView根据不同的securityMode inflate出来,并添加到界面上的。那么本文我...

天王盖地虎626
27分钟前
1
0
LNMP 环境搭建(Linux Nginx MariaDB PHP)

安装顺序:MariaDB-->PHP-->Nginx 一、MariaDB安装 官方下载网站:https://downloads.mariadb.org/ 1. 获取MySQL 下载:wget http://mirrors.neusoft.edu.cn/mariadb//mariadb-10.2.8/bintar......

Yue_Chen
30分钟前
1
0
MySQL中授权(grant)和撤销授权(revoke

MySQL中授权(grant)和撤销授权(revoke MySQL 赋予用户权限命令的简单格式可概括为: 1 grant 权限 on 数据库对象 to 用户   一、grant 普通数据用户,查询、插入、更新、删除 数据...

linjin200
33分钟前
1
0
你分得清楚Maven的聚合和继承吗?

用了 Maven 好几年了,许多人还是只懂得简单的依赖坐标。对于 Maven 的聚合和继承还是一知半解,甚至很多人以为是同一个东西。但其实聚合是用于快速构建项目,是表示项目与子项目之间的关系。...

java菜分享
35分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部