本文首发于 blog.zhaochunqi.com 转载请注明 blog.zhaochunqi.comjava
根据JSR 335, Java 终于在 Java 8 中引入了 Lambda 表达式。也称之为闭包或者匿名函数。python
所谓的 JSR (Java Specification Requests) 全称叫作 Java 规范提案。简单来讲就是向 Java 社区提交新的 API 或 服务 请求的提案。这些提案将做为 Java 社区进行 Java 语言开发的需求,引导着开发的方向。express
JSR 335 的提案内容摘要以下:编程
This JSR will extend the Java Programming Language Specification and the Java Virtual Machine Specification to support the following features:闭包
也就是以下几点:jvm
在 Java 8 中,以上均已经实现,以上内容下文均有介绍。ide
Lambda 表达式,其实就是代码块。函数
在具体了解 lambda 以前,咱们先日后退一步,看看以前咱们是如何处理这些代码块的!this
当决定在单独的线程运行某程序时,你这样作的spa
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 表达式!
考虑下前面的例子:
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; }
这样是非法的。
要介绍 Java 中 lambda 表达式的实现,须要知道什么是 函数式接口。
什么叫做函数式接口呢(SAM)?
函数式接口指的是只定义了惟一的抽象方法的接口(除了隐含的Object对象的公共方法), 所以最开始也就作SAM类型的接口(Single Abstract Method)。
Lambda 表达式向前兼容这些接口。
举个例子 Array.sort:
Arrays.sort(words, (first, second) -> Integer.compare(first.length(), second.length()));
Array.sort() 方法收到一个实现了 Comparable<String> 接口的实例。
其实能够把 Lambda 表达式想象成一个方法,而非一个对象,一个能够传入一个接口的方法。
再举个例子
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)
能不能再简洁一点?有的时候咱们所要作的事情不过是调用其余类中方法来处理事件。
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)
如上所见 ::
操做符将方法名与实例或者类分隔开。整体来讲,又以下的规则:
值得指出的是, 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(); } }
跟上一个差很少,毕竟构造方法 也是方法啊!!不过方法名字为 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
因为历史缘由,像是相似 Collection 这种接口,若是进行添加接口的话,那将会形成以前的代码出错。
Java 想了一个一劳永逸的方法解决这个问题, 使用 default 修饰符来提供默认的实现
好比 Collection 接口的源代码:
default void remove() { throw new UnsupportedOperationException("remove"); }
当没有 override remove 这个方法是,调用的时候返回 UnsupportedOperationException 错误。
Java 8 中,你能够在接口中添加静态方法了。 可能与你想的不太同样,可是呢,为了方便起见,如今 interface 能够有静态方法了。
参考连接: