Java 终于在 Java 8 中引入了 Lambda 表达式。也称之为闭包或者匿名函数。
本文首发于 blog.zhaochunqi.com 转载请注明 blog.zhaochunqi.com
根据JSR 335, Java 终于在 Java 8 中引入了 Lambda 表达式。也称之为闭包或者匿名函数。

JSR 335
所谓的 JSR (Java Specification Requests) 全称叫做 Java 规范提案。简单来说就是向 Java 社区提交新的 API 或 服务 请求的提案。这些提案将作为 Java 社区进行 Java 语言开发的需求,引导着开发的方向。
JSR 335 的提案内容摘要如下:
This JSR will extend the Java Programming Language Specification and the Java Virtual Machine Specification to support the following features:
- Lambda Expressions
- SAM Conversion
- Method References
- Virtual Extension Methods
也就是如下几点:
- 支持 lambda 表达式。
- 支持 SAM conversion 用来向前兼容。
- 方法引用 Method References
- Virtual Extension Methods
在 Java 8 中,以上均已经实现,以上内容下文均有介绍。
为什么需要 Lambda 表达式?
Lambda 表达式,其实就是代码块。

原来怎么处理
在具体了解 lambda 之前,我们先往后退一步,看看之前我们是如何处理这些代码块的!
例子一
当决定在单独的线程运行某程序时,你这样做的
class Worker implements Runnable {
public void run() {
for (int i = 0; i < 1000; i++)
doWork();
}
...
}
这样执行:
Worker w = new Worker();
new Thread(w).start();
Worker 中包含了你要执行的代码块。
例子二
如果你想实现根据字符串长度大小来排序,而不是默认的字母顺序,你可以自己来实现一个 Comparator 用来 Sort。
class LengthComparator implements Comparator<String> {
public int compare(String first, String second) {
return Integer.compare(first.length(), second.length());
}
}
Arrays.sort(strings, new LengthComparator());
例子三
另外一个例子,我选的是 Android 中的点击事件,同样是 Java:
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(MainActivity.this, "Hello World!", Toast.LENGTH_SHORT).show();
}
});
上面代码有什么问题呢?

它们都太复杂了啊!
上述例子都是在某个类中实现某个接口,然后传递到另外一个方法中作为参数,然后用来执行。
但是本质上,他们要传递的就是接口中那一个方法的实现而已啊!有必要先创建类,再实例化,再传递给调用的位置吗?
因为 Java 是纯面向对象的语言,像其他语言那样随随便便传个方法过来,那可不行,必须要这样。
在其他语言中你可能可以,但是,在Java 中,不可以。

Java 设计人员为了 Java 的简洁跟连贯性,一直拒绝为Java添加这种功能。(这也是我喜欢Java而不喜欢Python的原因啊!!!)
经过多年的努力,开发人员终于找到了符合 Java 编程习惯的 Lambda 表达式!
Lambda 表达式语法(Syntax)
考虑下前面的例子:
Integer.compare(first.length(), second.length())
first和second都是 String 类型,Java 是强类型的语言,必须指定类型:
(String first, String second)
-> Integer.compare(first.length(), second.length())

看到没有!第一个 Lambda 表达式诞生了!!输入、输出简洁明了!
为什么叫 Lambda 呢,这个很多年以前,有位逻辑学家想要标准化的表示一些可以被计算的数学方程(实际上存在,但是很难被表示出来),他就用 ℷ 来表示。
重新介绍一下 Java 中 Lambda 表达式的格式:
(参数) -> 表达式
多返回值
如果计算的结果并不由一个单一的表达式返回(换言之,返回值存在多种情况),使用“{}",然后明确指定返回值。
(String first, String second) -> {
if (first.length() < second.length()) return -1;
else if (first.length() > second.length()) return 1;
else return 0;
}
无参数
如果没有参数,则 "()"中就空着。
() -> { for (int i = 0; i < 1000; i++) doWork(); }
省略
如果参数的类型可以被推断出,则可以直接省略
Comparator<String> comp
= (first, second) // Same as (String first, String second)
-> Integer.compare(first.length(), second.length());
这里,first和second可以被推断出是 String 类型,因为 是一个 String 类型的 Comparator。
如果单个参数可以被推断出,你连括号都可以省略:
EventHandler<ActionEvent> listener = event ->
System.out.println("Thanks for clicking!");
// Instead of (event) -> or (ActionEvent event) ->
修饰符
你可以像对待其他方法一样,annotation,或者 使用 final 修饰符
(final String name) -> ...
(@NonNull String name) -> ...
永远不要定义 result 的类型,lambda 表达式总是从上下文中推断出来的:
(String first, String second) -> Integer.compare(first.length(), second.length())
注意
注意,在lambda 表达式中,某些分支存在返回值,某些不存在返回值这样的情况是不允许的。
如 (int x) -> { if (x >= 0) return 1; }
这样是非法的。
函数式接口(Functional Interfaces/SAM)
要介绍 Java 中 lambda 表达式的实现,需要知道什么是 函数式接口。
什么叫作函数式接口呢(SAM)?
函数式接口指的是只定义了唯一的抽象方法的接口(除了隐含的Object对象的公共方法), 因此最开始也就做SAM类型的接口(Single Abstract Method)。
Lambda 表达式向前兼容这些接口。
Comparable
举个例子 Array.sort:
Arrays.sort(words,
(first, second) -> Integer.compare(first.length(), second.length()));
Array.sort() 方法收到一个实现了 Comparable<String> 接口的实例。
其实可以把 Lambda 表达式想象成一个方法,而非一个对象,一个可以传入一个接口的方法。
OnClickListener
再举个例子
button.setOnClickListener(event ->
System.out.println("Thanks for clicking!"));
你看,是不是更易读了呢?
Lambda 表达式能够向前兼容这些 interfaces, 太棒了! 那 Lambda 表达式还能干什么呢?
实际上,将函数式接口转变成 lambda 表达式是你在 Java 中唯一能做的事情。

Why ?!!
在其他的语言中,你可以定义一些方便的方法类型,但在 Java 中,你甚至不能将一个Lambda表达式赋值给类型为 Object 的变量,因为 Object 变量不是一个 Functional Interface。
Java 的设计者们坚持使用熟悉的 interface 概念而不是为其引入新的 方法类型。
(这里我还要为设计者点赞!谨慎的设计,一方面降低了初学者的门槛,一方面方便了高级用户的使用。对比 python2和 python3,升级的不兼容让很多人一直停留在 python2)
Method References
能不能再简洁一点?有的时候我们所要做的事情不过是调用其他类中方法来处理事件。
button.setOnClickListener(event -> System.out.println(event));
如果这样呢?
button.setOnAction(System.out::println);
表达式 System.out::println
属于一个方法引用(method reference), 相当于 lambda 表达式 x -> System.out.println(x)

再举个例子,如果你想对字符串不管大小写进行排序,就可以这样写!
Arrays.sort(strings, String::compareToIgnoreCase)
如上所见 ::
操作符将方法名与实例或者类分隔开。总体来说,又如下的规则:
- object::instanceMethod
- Class::staticMethod
- Class::instanceMethod
值得指出的是, this
和super
关键字可以在其中使用:
class Greeter {
public void greet() {
System.out.println("Hello, world!");
}
}
class ConcurrentGreeter extends Greeter {
public void greet() {
Thread t = new Thread(super::greet);
t.start();
}
}
构造方法引用 Constructor References
跟上一个差不多,毕竟构造方法 也是方法啊!!不过方法名字为 new 。
但是!这个构造方法引用有一个牛逼的地方!
你知道 Array 是不能使用范型的对吧!(什么,你不知道?看看这里 http://stackoverflow.com/questions/2927391/whats-the-reason-i-cant-create-generic-array-types-in-java),你没有办法创建一个类型为 T 的 Array 。 new T[n] 将会被覆盖为 new Object[n]。
假设我们想要一个包含 buttons 的 Array。Stream interface 可以返回一个 Object array。
Object[] buttons = stream.toArray();
不不不,我们可不想要 Object。Stream 库使用 构造方法引用解决了这个问题:
Button[] buttons = stream.toArray(Button[]::new);

变量作用域
注意到我们在题目中写着 闭包(closure),实际上,闭包的定义是: 引用了自由变量的函数。
在之前,如果需要在匿名类的内部引用外部变量,需要将外部变量定义为 final ,现在有了 lambda 表达式,你不必再这么做了。但同样需要保证外部的自由变量不能在 lambda 表达式中被改变。

这是什么意思呢? 不需要定义为 final,也不能改?
其实理解起来很简单,Java 8 中,不需要定义为 final ,但你其实可以直接把他当作 final,不要试图修改它就行了。
即便你用内部类,现在也无需定义为 final 了。
参考 StackOverFlow 链接: http://stackoverflow.com/questions/4732544/why-are-only-final-variables-accessible-in-anonymous-class
Default Methods
由于历史原因,像是类似 Collection 这种接口,如果进行添加接口的话,那将会造成之前的代码出错。
Java 想了一个一劳永逸的方法解决这个问题, 使用 default 修饰符来提供默认的实现
比如 Collection 接口的源代码:
default void remove() {
throw new UnsupportedOperationException("remove");
}
当没有 override remove 这个方法是,调用的时候返回 UnsupportedOperationException 错误。
Static Methods in Interfaces
Java 8 中,你可以在接口中添加静态方法了。 可能与你想的不太一样,但是呢,为了方便起见,现在 interface 可以有静态方法了。
参考链接:
- JSR 335: Lambda Expressions for the JavaTM Programming Language
- Java 8 新特性概述
- Lambda Expressions in Java 8

作者:AlexZhao
链接:https://www.jianshu.com/p/85affe38ce5c
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
Java 终于在 Java 8 中引入了 Lambda 表达式。也称之为闭包或者匿名函数。的更多相关文章
- 《Java 8 in Action》Chapter 3:Lambda表达式
1. Lambda简介 可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表.函数主体.返回类型,可能还有一个可以抛出的异常列表. 匿名--我们说匿名,是因为 ...
- IDEA中打包Spark项目提示Error:(16, 48) java: -source 1.5 中不支持 lambda 表达式
在idea中新建了一Spark的项目,在做项目的编译打包的时候,提示如下错误信息: Error:(, ) java: -source 1.5 中不支持 lambda 表达式 (请使用 -source ...
- IDEA无法编译java8的lambda表达式提示Error:(16, 48) java: -source 1.5 中不支持 lambda 表达式
在idea中新建了一个java8的项目,但是写lambda表达式提示语法错误,提示如下错误信息: Error:(16, 48) java: -source 1.5 中不支持 lambda 表达式 (请 ...
- Java 8新特性(一):Lambda表达式
2014年3月发布的Java 8,有可能是Java版本更新中变化最大的一次.新的Java 8为开发者带来了许多重量级的新特性,包括Lambda表达式,流式数据处理,新的Optional类,新的日期和时 ...
- maven编译报错 -source 1.5 中不支持 lambda 表达式
在用maven编译项目是由于项目中用了jdk 1.8, 编译是报错 -source 1.5 中不支持 lambda 表达式,Google找到这篇解决方案,记录一下: 编译时报如下错误: [ERROR ...
- maven编译报错 -source 1.7 中不支持 lambda 表达式
Maven项目编译失败: [ERROR] COMPILATION ERROR : [INFO] ---------------------------------------------------- ...
- maven编译报错 -source 1.5 中不支持 lambda 表达式(转)
原文链接:http://blog.csdn.net/kai161/article/details/50379418 在用maven编译项目是由于项目中用了jdk 1.8, 编译是报错 -source ...
- PHP中的闭包和匿名函数
闭包的概念是指在创建闭包时,闭包会封装周围的状态的函数.即便闭包所在环境不在了.但闭包中封装的状态依然存在. 匿名函数就是没有名称的函数. 它们看似很函数一样,实际上它们属于Closure类的实例 P ...
- Android 1.7 中不支持 lambda 表达式
Error:(129, 32) 错误: -source 1.7 中不支持 lambda 表达式 (请使用 -source 8 或更高版本以启用 lambda 表达式) lambda expressio ...
随机推荐
- 【爬虫】大杀器——phantomJS+selenium
[爬虫]大杀器——phantomJS+selenium 视频地址 江湖上有一个传说,得倚天屠龙者可称霸武林.爬虫中也有两个大杀器,他们结合在一起时,无往不利,不管你静态网站还是动态网站,通吃. pha ...
- 用LinkedList和ArrayList实现自定义栈的异同
//ArrayList已连续的空间进行存储数据 //LinkedList已链表的结构存储数据 //栈 MyStark ms=new MyStark();//new 一个实现栈的类 //压栈 ...
- MCMC蒙特卡罗马尔科夫模型
https://www.cnblogs.com/pinard/p/6645766.html https://blog.csdn.net/saltriver/article/details/521949 ...
- 1.5 synchronized其他概念
synchronized锁重入: 关键字synchronized拥有锁重入的功能,也就是使用synchronized时,当一个线程得到了一个对象的锁后,再次请求此对象时是可以再次得到对象的锁. 输出结 ...
- 洛谷 P2872 [USACO07DEC]道路建设Building Roads 题解
P2872 [USACO07DEC]道路建设Building Roads 题目描述 Farmer John had just acquired several new farms! He wants ...
- “猜你喜欢”的背后揭秘--10分钟教你用Python打造推荐系统
欲直接下载代码文件,关注我们的公众号哦!查看历史消息即可! 话说,最近的瓜实在有点多,从我科校友李雨桐怒锤某男.陈羽凡吸毒被捕.蒋劲夫家暴的三连瓜,到不知知网翟博士,再到邓紫棋解约蜂鸟.王思聪花千芳隔 ...
- ELK教程2:Kibana的安装
kibana作为ElastciSearch的数据查询展示界面,集成了很多的功能,本文主要讲述如下部署kibana. 安装 安装命令如下: # 下载kibana的npm wget https://art ...
- Vue中的$emit组件事件运用
话不多说上代码 vue>src>App.vue <template> <div id="app"> <!-- header --> ...
- 内核中通过进程PID获取进程的全部路径
目录 一丶简介 二丶原理 1.原理 2.代码实现. 一丶简介 我们遇到的Dos路径.如果想转化为NT路径(也就是 C:\xxxx)类似的格式 需要自己实现. 具体原理如下: 二丶原理 1.原理 1.使 ...
- 6、httpd2.4 编译安装LAMP
www.itjc8.com 新特性: MPM支持运营DSO机制(动态共享对象),以模块形式按需加载 支持event MPM 支持异步读写 支持每模块及每个目录分别使用各自的日志级别 每请求配置 增强版 ...