理解Functional Interface(函数式接口,如下简称FI)是学习Java8 Lambda表达式的关键所在,因此放在最开始讨论。FI的定义其实很简单:任何接口,若是只包含惟一一个抽象方法,那么它就是一个FI。为了让编译器帮助咱们确保一个接口知足FI的要求(也就是说有且仅有一个抽象方法),Java8提供了@FunctionalInterface注解。举个简单的例子,Runnable接口就是一个FI,下面是它的源代码:html
为了可以方便、快捷、幽雅的建立出FI的实例,Java8提供了Lambda表达式这颗语法糖。下面我用一个例子来介绍Lambda语法。假设咱们想对一个List<String>按字符串长度进行排序,那么在Java8以前,能够借助匿名内部类来实现:java
上面的匿名内部类简直能够用丑陋来形容,惟一的一行逻辑被五行垃圾代码淹没。根据前面的定义(并查看Java源代码)可知,Comparator是个FI,因此,能够用Lambda表达式来实现:程序员
代码变短了好多!仔细观察就会发现,Lambda表达式,很像一个匿名的方法,只是圆括号内的参数列表和花括号内的代码被->分隔开了。垃圾代码写的越少,咱们就有越多的时间去写真正的逻辑代码,不是吗?是的!圆括号里的参数类型是能够省略的:express
若是Lambda表达式的代码块只是return后面跟一个表达式,那么还能够进一步简化:编程
注意,表达式后面是没有分号的!若是只有一个参数,那么包围参数的圆括号能够省略:oracle
若是表达式不须要参数呢?好吧,那也必须有圆括号,例如:app
有时候Lambda表达式的代码就只是一个简单的方法调用而已,遇到这种状况,Lambda表达式还能够进一步简化为方法引用(Method References)。一共有四种形式的方法引用,第一种引用静态方法,例如:ide
第二种引用某个特定对象的实例方法,例如前面那个遍历并打印每个word的例子能够写成这样:函数式编程
第三种引用某个类的实例方法,例如:函数
第四种引用类的构造函数,例如:
既然Lambda表达式这么好用,那么,能够在哪些地方使用呢?若是你真正明白了什么是FI(很容易),应该马上就能给出答案:任何能够接受一个FI实例的地方,均可以用Lambda表达式。好比,虽然上面给出的例子都是把Lambda表达式看成方法参数传递,但实际上你也能够定义变量:
Java8除了给Runnable,Comparator等接口打上了@FunctionalInterface注解以外,还预约义了一大批新的FI。这些接口都在java.util.function包里,下面简单介绍其中的几个。
Predicate用来判断一个对象是否知足某种条件,好比,单词是否由六个以上字母组成:
Function表示接收一个参数,并产生一个结果的函数:
下面的例子将集合里的每个整数都乘以2:
Consumer表示对单个参数进行的操做,前面例子中的forEach()方法接收的参数就是这种操做:
为了充分发挥Lambda的威力,Java8对不少老的类库进行了加强,给它们配备了Lambda武器。好比前面例子中用到的forEach()方法,其实是添加到Iterable接口中的。而屡次出现的stream()方法,则是添加在了Collection接口里。用过Ruby,Scala,Groovy等语言的Java程序员,可能已经对在这些语言里很好实现的外部迭代器模式垂涎好久了。虽然Google的Guava能够在必定程度上弥补Java的这种缺陷,可是Java8的Lambda才真正让Java朝着函数式编程迈进了一大步。
细心的读者可能会发现一个问题,给Iterable和Collection等接口增长方法,岂不是会破坏接口的向后兼容性?是的,为了保证API的向后兼容性,Java8对接口的语法进行了较大的调整,增长了默认方法(Default Methods)。下面是forEach()方法的实现代码:
除了抽象方法和默认方法,从Java8开始,接口也能够有静态(static)方法了。有了这个语法,咱们就能够把和接口相关的帮助方法(Helper Methods)直接定义在接口里了。好比Function接口就定义了一个工厂方法indentity():
和内部类同样,Lambda也能够访问外部(词法做用域)变量,规则基本同样。Java8以前,内部类只能访问final类型的变量,Java8放宽了这种限制,只要变量实际上不可变(effectively final)就能够。换句话说,若是你给变量加上final关键字编译器也不报错,那么去掉final关键字后,它就是effectively final的。看下面的例子:
在Java8以前,a必须是final的才能被x看到。下面用Lambda表达式重写上面的例子:
能够看到,为了支持Lambda表达式,Java8对Java语言作了很大的调整。但Lambda表达式并不是只是Java语法糖,而是由编译器和JVM共同配合来实现的,这一点我会在下一篇文章里详细介绍