14.1 lambda表达式
lambda表达式是Java开发人员长期以来一直期待Java语言增加的一个语法特性。使用lambda表达式可以通过简洁的代码来进行计算,减少了不必要的冗余代码。lambda表达式主要用在两个地方:一个是操作新的集合类框架实现,另外一个是更好地使用已有的函数式接口。lambda表达式实际上是创建函数式接口的一种简化的方式。lambda表达式的相关内容由JSR 335来规范。
在接下来的内容中,先介绍可以用lambda表达式来实现的函数式接口,再对lambda表达式的语法进行介绍。通过目标类型,编译器可以推断出lambda表达式所实现的接口类型,并进行必要的合法性检验。lambda表达式中的代码与包围它的代码块在同一个词法作用域中,这会对代码中的名称解析产生影响。已有类中的方法也可以通过方法引用的方式作为lambda表达式来使用。接口的默认方法则提供了一种与已有实现相兼容的方式来更新接口的定义。
14.1.1 函数式接口
Java是一种面向对象的编程语言。在开发程序时,程序中的代码逻辑被封装在不同的类中。Java类通常既包括用来维护内部状态的域,又包括用来执行业务逻辑和更新内部状态的方法。在有些情况下,程序所要做的只是简单地执行一段代码。比如在进行排序操作时,最核心的逻辑是判断任意两个对象之间的先后顺序。从函数的角度来说,这段代码接受两个对象作为输入,返回表示两个对象的先后顺序的值作为输出。在Java SE 7中,Java语言并没有提供直接的语法结构来表示一个函数,与函数相近的结构是方法,但是方法不能脱离类而单独存在。因此,对于这样的场景,程序的一般做法是先定义一个接口,再定义接口的实现类。在需要使用方法时,先创建该实现类的对象,再调用对象中的方法。这个完整的过程比较繁琐。通过Java提供的匿名内部类可以简化一些操作。以java.lang.Runnable接口为例,当需要创建一个线程并启动该线程时,一般的做法如代码清单14-1所示。
代码清单14-1 Runnable接口的一般使用示例
new Thread(new Runnable(){
public void run(){
System.out.println("Hello World!");
}
}).start();
从上面的代码可以看到,真正执行程序逻辑的有意义的代码其实只有1行,剩下的4行代码都是为了满足Java的语法要求而加上的,这就造成了一定的代码冗余。类似这样的代码在程序中会经常见到。由于相关的逻辑通常只在一个地方出现,没有必要把这些代码封装在另外一个单独的类中,因此,使用匿名内部类就成了开发人员最常见的选择。
Java标准库中还存在其他与Runnable接口类似的接口,这些接口的特征是只声明了一个方法。具备这样特征的接口被称为函数式接口(functional interface)。对于函数式接口,通过匿名内部类实现的代码形式可以进一步简化。在代码清单14-1中,对Runnable接口中的run方法的声明是可以被去掉的。Runnable接口中只包含一个方法,因此所包含的代码肯定是对这个唯一的方法的实现。同时对Runnable接口本身的声明也是可以被去掉的,可以从使用该匿名内部类的上下文环境中推断出使用的接口的具体类型。Java SE 8中的lambda表达式利用了函数式接口的特点,提供了一种比匿名内部类更加简洁的方式来实现函数式接口。
Java标准库中存在的函数式接口比较多,常见的包括Runnable、java.util.Comparator、java.io.FileFilter、java.util.concurrent.Callable、java.lang.reflect.InvocationHandler、java.beans.PropertyChangeListener、java.awt.event.ActionListener和javax.swing.event.ChangeListener等。程序也可以定义自己的函数式接口。