本文首发于 blog.zhaochunqi.com 转载请注明 blog.zhaochunqi.com

根据JSR 335, Java 终于在 Java 8 中引入了 Lambda 表达式。也称之为闭包或者匿名函数。

 
http://harchiko.qiniudn.com/Lambda%20Expression%20Java%208.png

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

也就是如下几点:

  1. 支持 lambda 表达式。
  2. 支持 SAM conversion 用来向前兼容。
  3. 方法引用 Method References
  4. Virtual Extension Methods

在 Java 8 中,以上均已经实现,以上内容下文均有介绍。

为什么需要 Lambda 表达式?

Lambda 表达式,其实就是代码块。

 
http://harchiko.qiniudn.com/56cabf5a499ed708%202.jpg

原来怎么处理

在具体了解 lambda 之前,我们先往后退一步,看看之前我们是如何处理这些代码块的!

例子一

当决定在单独的线程运行某程序时,你这样做的

  1. class Worker implements Runnable {
  2. public void run() {
  3. for (int i = 0; i < 1000; i++)
  4. doWork();
  5. }
  6. ...
  7. }

这样执行:

  1. Worker w = new Worker();
  2. new Thread(w).start();

Worker 中包含了你要执行的代码块。

例子二

如果你想实现根据字符串长度大小来排序,而不是默认的字母顺序,你可以自己来实现一个 Comparator 用来 Sort。

  1. class LengthComparator implements Comparator<String> {
  2. public int compare(String first, String second) {
  3. return Integer.compare(first.length(), second.length());
  4. }
  5. }
  6. Arrays.sort(strings, new LengthComparator());

例子三

另外一个例子,我选的是 Android 中的点击事件,同样是 Java:

  1. button.setOnClickListener(new View.OnClickListener() {
  2. @Override
  3. public void onClick(View view) {
  4. Toast.makeText(MainActivity.this, "Hello World!", Toast.LENGTH_SHORT).show();
  5. }
  6. });

上面代码有什么问题呢?

 
http://harchiko.qiniudn.com/c718cee7.jpg

它们都太复杂了啊!

上述例子都是在某个类中实现某个接口,然后传递到另外一个方法中作为参数,然后用来执行。

但是本质上,他们要传递的就是接口中那一个方法的实现而已啊!有必要先创建类,再实例化,再传递给调用的位置吗?

因为 Java 是纯面向对象的语言,像其他语言那样随随便便传个方法过来,那可不行,必须要这样。

在其他语言中你可能可以,但是,在Java 中,不可以。

 
http://harchiko.qiniudn.com/56cabf7011ab6750.jpg

Java 设计人员为了 Java 的简洁跟连贯性,一直拒绝为Java添加这种功能。(这也是我喜欢Java而不喜欢Python的原因啊!!!)

经过多年的努力,开发人员终于找到了符合 Java 编程习惯的 Lambda 表达式!

Lambda 表达式语法(Syntax)

考虑下前面的例子:

  1. Integer.compare(first.length(), second.length())

first和second都是 String 类型,Java 是强类型的语言,必须指定类型:

  1. (String first, String second)
  2. -> Integer.compare(first.length(), second.length())
 
http://harchiko.qiniudn.com/14365393725281065.jpg

看到没有!第一个 Lambda 表达式诞生了!!输入、输出简洁明了!

为什么叫 Lambda 呢,这个很多年以前,有位逻辑学家想要标准化的表示一些可以被计算的数学方程(实际上存在,但是很难被表示出来),他就用 ℷ 来表示。

重新介绍一下 Java 中 Lambda 表达式的格式:

(参数) -> 表达式

多返回值

如果计算的结果并不由一个单一的表达式返回(换言之,返回值存在多种情况),使用“{}",然后明确指定返回值。

  1. (String first, String second) -> {
  2. if (first.length() < second.length()) return -1;
  3. else if (first.length() > second.length()) return 1;
  4. else return 0;
  5. }

无参数

如果没有参数,则 "()"中就空着。

  1. () -> { for (int i = 0; i < 1000; i++) doWork(); }

省略

如果参数的类型可以被推断出,则可以直接省略

  1. Comparator<String> comp
  2. = (first, second) // Same as (String first, String second)
  3. -> Integer.compare(first.length(), second.length());

这里,first和second可以被推断出是 String 类型,因为 是一个 String 类型的 Comparator。

如果单个参数可以被推断出,你连括号都可以省略:

  1. EventHandler<ActionEvent> listener = event ->
  2. System.out.println("Thanks for clicking!");
  3. // Instead of (event) -> or (ActionEvent event) ->

修饰符

你可以像对待其他方法一样,annotation,或者 使用 final 修饰符

  1. (final String name) -> ...
  2. (@NonNull String name) -> ...

永远不要定义 result 的类型,lambda 表达式总是从上下文中推断出来的:

  1. (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:

  1. Arrays.sort(words,
  2. (first, second) -> Integer.compare(first.length(), second.length()));

Array.sort() 方法收到一个实现了 Comparable<String> 接口的实例。

其实可以把 Lambda 表达式想象成一个方法,而非一个对象,一个可以传入一个接口的方法。

OnClickListener

再举个例子

  1. button.setOnClickListener(event ->
  2. System.out.println("Thanks for clicking!"));

你看,是不是更易读了呢?

Lambda 表达式能够向前兼容这些 interfaces, 太棒了! 那 Lambda 表达式还能干什么呢?

实际上,将函数式接口转变成 lambda 表达式是你在 Java 中唯一能做的事情。

 
http://harchiko.qiniudn.com/20150930185659_eMZyN.jpeg

Why ?!!

在其他的语言中,你可以定义一些方便的方法类型,但在 Java 中,你甚至不能将一个Lambda表达式赋值给类型为 Object 的变量,因为 Object 变量不是一个 Functional Interface。

Java 的设计者们坚持使用熟悉的 interface 概念而不是为其引入新的 方法类型。

(这里我还要为设计者点赞!谨慎的设计,一方面降低了初学者的门槛,一方面方便了高级用户的使用。对比 python2和 python3,升级的不兼容让很多人一直停留在 python2)

Method References

能不能再简洁一点?有的时候我们所要做的事情不过是调用其他类中方法来处理事件。

  1. button.setOnClickListener(event -> System.out.println(event));

如果这样呢?

  1. button.setOnAction(System.out::println);

表达式 System.out::println 属于一个方法引用(method reference), 相当于 lambda 表达式 x -> System.out.println(x)

 
http://harchiko.qiniudn.com/20151220232425_nWH23.jpeg

再举个例子,如果你想对字符串不管大小写进行排序,就可以这样写!

  1. Arrays.sort(strings, String::compareToIgnoreCase)

如上所见 ::操作符将方法名与实例或者类分隔开。总体来说,又如下的规则:

  • object::instanceMethod
  • Class::staticMethod
  • Class::instanceMethod

值得指出的是, thissuper关键字可以在其中使用:

  1. class Greeter {
  2. public void greet() {
  3. System.out.println("Hello, world!");
  4. }
  5. }
  1. class ConcurrentGreeter extends Greeter {
  2. public void greet() {
  3. Thread t = new Thread(super::greet);
  4. t.start();
  5. }
  6. }

构造方法引用 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。

  1. Object[] buttons = stream.toArray();

不不不,我们可不想要 Object。Stream 库使用 构造方法引用解决了这个问题:

  1. Button[] buttons = stream.toArray(Button[]::new);
 
http://harchiko.qiniudn.com/Screen%20Shot%202016-11-16%20at%204.30.23%20AM.png

变量作用域

注意到我们在题目中写着 闭包(closure),实际上,闭包的定义是: 引用了自由变量的函数。

在之前,如果需要在匿名类的内部引用外部变量,需要将外部变量定义为 final ,现在有了 lambda 表达式,你不必再这么做了。但同样需要保证外部的自由变量不能在 lambda 表达式中被改变。

 
http://harchiko.qiniudn.com/56cabf5d7d6dc247.jpg!600x600.jpg

这是什么意思呢? 不需要定义为 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 接口的源代码:

  1. default void remove() {
  2. throw new UnsupportedOperationException("remove");
  3. }

当没有 override remove 这个方法是,调用的时候返回 UnsupportedOperationException 错误。

Static Methods in Interfaces

Java 8 中,你可以在接口中添加静态方法了。 可能与你想的不太一样,但是呢,为了方便起见,现在 interface 可以有静态方法了。

参考链接:

  1. JSR 335: Lambda Expressions for the JavaTM Programming Language
  2. Java 8 新特性概述
  3. Lambda Expressions in Java 8
 

作者:AlexZhao
链接:https://www.jianshu.com/p/85affe38ce5c
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

Java 终于在 Java 8 中引入了 Lambda 表达式。也称之为闭包或者匿名函数。的更多相关文章

  1. 《Java 8 in Action》Chapter 3:Lambda表达式

    1. Lambda简介 可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表.函数主体.返回类型,可能还有一个可以抛出的异常列表. 匿名--我们说匿名,是因为 ...

  2. IDEA中打包Spark项目提示Error:(16, 48) java: -source 1.5 中不支持 lambda 表达式

    在idea中新建了一Spark的项目,在做项目的编译打包的时候,提示如下错误信息: Error:(, ) java: -source 1.5 中不支持 lambda 表达式 (请使用 -source ...

  3. IDEA无法编译java8的lambda表达式提示Error:(16, 48) java: -source 1.5 中不支持 lambda 表达式

    在idea中新建了一个java8的项目,但是写lambda表达式提示语法错误,提示如下错误信息: Error:(16, 48) java: -source 1.5 中不支持 lambda 表达式 (请 ...

  4. Java 8新特性(一):Lambda表达式

    2014年3月发布的Java 8,有可能是Java版本更新中变化最大的一次.新的Java 8为开发者带来了许多重量级的新特性,包括Lambda表达式,流式数据处理,新的Optional类,新的日期和时 ...

  5. maven编译报错 -source 1.5 中不支持 lambda 表达式

    在用maven编译项目是由于项目中用了jdk 1.8, 编译是报错  -source 1.5 中不支持 lambda 表达式,Google找到这篇解决方案,记录一下: 编译时报如下错误: [ERROR ...

  6. maven编译报错 -source 1.7 中不支持 lambda 表达式

    Maven项目编译失败: [ERROR] COMPILATION ERROR : [INFO] ---------------------------------------------------- ...

  7. maven编译报错 -source 1.5 中不支持 lambda 表达式(转)

    原文链接:http://blog.csdn.net/kai161/article/details/50379418 在用maven编译项目是由于项目中用了jdk 1.8, 编译是报错  -source ...

  8. PHP中的闭包和匿名函数

    闭包的概念是指在创建闭包时,闭包会封装周围的状态的函数.即便闭包所在环境不在了.但闭包中封装的状态依然存在. 匿名函数就是没有名称的函数. 它们看似很函数一样,实际上它们属于Closure类的实例 P ...

  9. Android 1.7 中不支持 lambda 表达式

    Error:(129, 32) 错误: -source 1.7 中不支持 lambda 表达式 (请使用 -source 8 或更高版本以启用 lambda 表达式) lambda expressio ...

随机推荐

  1. 【爬虫】大杀器——phantomJS+selenium

    [爬虫]大杀器——phantomJS+selenium 视频地址 江湖上有一个传说,得倚天屠龙者可称霸武林.爬虫中也有两个大杀器,他们结合在一起时,无往不利,不管你静态网站还是动态网站,通吃. pha ...

  2. 用LinkedList和ArrayList实现自定义栈的异同

    //ArrayList已连续的空间进行存储数据  //LinkedList已链表的结构存储数据    //栈  MyStark ms=new MyStark();//new 一个实现栈的类  //压栈 ...

  3. MCMC蒙特卡罗马尔科夫模型

    https://www.cnblogs.com/pinard/p/6645766.html https://blog.csdn.net/saltriver/article/details/521949 ...

  4. 1.5 synchronized其他概念

    synchronized锁重入: 关键字synchronized拥有锁重入的功能,也就是使用synchronized时,当一个线程得到了一个对象的锁后,再次请求此对象时是可以再次得到对象的锁. 输出结 ...

  5. 洛谷 P2872 [USACO07DEC]道路建设Building Roads 题解

    P2872 [USACO07DEC]道路建设Building Roads 题目描述 Farmer John had just acquired several new farms! He wants ...

  6. “猜你喜欢”的背后揭秘--10分钟教你用Python打造推荐系统

    欲直接下载代码文件,关注我们的公众号哦!查看历史消息即可! 话说,最近的瓜实在有点多,从我科校友李雨桐怒锤某男.陈羽凡吸毒被捕.蒋劲夫家暴的三连瓜,到不知知网翟博士,再到邓紫棋解约蜂鸟.王思聪花千芳隔 ...

  7. ELK教程2:Kibana的安装

    kibana作为ElastciSearch的数据查询展示界面,集成了很多的功能,本文主要讲述如下部署kibana. 安装 安装命令如下: # 下载kibana的npm wget https://art ...

  8. Vue中的$emit组件事件运用

    话不多说上代码 vue>src>App.vue <template> <div id="app"> <!-- header --> ...

  9. 内核中通过进程PID获取进程的全部路径

    目录 一丶简介 二丶原理 1.原理 2.代码实现. 一丶简介 我们遇到的Dos路径.如果想转化为NT路径(也就是 C:\xxxx)类似的格式 需要自己实现. 具体原理如下: 二丶原理 1.原理 1.使 ...

  10. 6、httpd2.4 编译安装LAMP

    www.itjc8.com 新特性: MPM支持运营DSO机制(动态共享对象),以模块形式按需加载 支持event MPM 支持异步读写 支持每模块及每个目录分别使用各自的日志级别 每请求配置 增强版 ...