Java SE 8: Lambda表达式2013-12-07 infoq 成富Java SE 8在6月13的版本中已经完全了全部的功能。在这些新的功能中,lambda表达式是推动该版本发布 的最重要新特性。因为Java第一次尝试引入函数式编程的相关内容。社区对于lambda表达式也期待已久。 Lambda表达式的相关内容在JSR 335中定义,本文的内容基于最新的规范和JDK 8 Build b94。 开发环境使用 的是Eclipse。Lambda表达式要理解lambda表达式,首先要了解的是函数式接口(functional interface)。简单来说,函数式接口是只包含一个抽象方法的接口。比如Java标准库中的 java.lang.Runnable和java.util.Comparator都是典型的函数式接口。对于函数式接口,除了可以使用Java中 标准的方法来创建实现对象之外,还可以使用lambda表达式来创建实现对象。这可以在很大程度上简化代码的 实现。在使用lambda表达式时,只需要提供形式参数和方法体。由于函数式接口只有一个抽象方法,所以通过 lambda表达式声明的方法体就肯定是这个唯一的抽象方法的实现,而且形式参数的类型可以根据方法的类型声 明进行自动推断。以Runnable接口为例来进行说明,传统的创建一个线程并运行的方式如下所示:
public void runThread() {new Thread(new Runnable() {public void run() {System.out.println("Run!");}}).start();}在上面的代码中,首先需要创建一个匿名内部类实现Runnable接口,还需要实现接口中的run方法。 如果使用lambda表达式来完成同样的功能,得到的代码非常简洁,如下面所示:
public void runThreadUseLambda() {new Thread(() -> {System.out.println("Run!");}).start();}相对于传统的方式,lambda表达式在两个方面进行了简化:首先是Runnable接口的声明,这可以通 过对上下文环境进行推断来得出;其次是对run方法的实现,因为函数式接口中只包含一个需要实现的方法。Lambda表达式的声明方式比较简单,由形式参数和方法体两部分组成,中间通过“->”分隔。形式 参数不需要包含类型声明,可以进行自动推断。当然在某些情况下,形式参数的类型声明是不可少的。方法体 则可以是简单的表达式或代码块。比如把一个整数列表按照降序排列可以用下面的代码来简洁实现:
Collections.sort(list, (x, y) -> y - x);
Lambda表达式“(x, y) -> y - x“ 实现了java.util.Comparator接口。在Java SE 8之前的标准库中包含的函数式接口并不多。Java SE 8增加了java.util.function包,里面都是可以在开发中使用的函数式接口。开发人员也可以创建新的函数式 接口。最好在接口上使用注解@FunctionalInterface进行声明,以免团队的其他人员错误地往接口中添加新的 方法。下面的代码使用函数式接口java.util.function.Function实现的对列表进行map操作的方法。 从代码中可以看到,如果尽可能的使用函数式接口,则代码使用起来会非常简洁。
public class CollectionUtils {public staticList map(List input, Function processor) {ArrayList result = new ArrayList();for (T obj : input) {result.add(processor.apply(obj));}return result;}public static void main(String[] args) {List input = Arrays.asList(new String[] {"apple", "orange", "pear"});List lengths = CollectionUtils.map(input, (String v) -> v.length());List uppercases = CollectionUtils.map(input, (String v) -> v.toUpperCase());}}方法和构造方法引用方法引用可以在不调用某个方法的情况下引用一个方法。构造方法引用可以在 不创建对象的情况下引用一个构造方法。方法引用是另外一种实现函数式接口的方法。在某些情况下,方法引 用可以进一步简化代码。比如下面的代码中,第一个forEach方法调用使用的是lambda表达式,第二个使用的 是方法引用。两者作用相同,不过使用方法引用的做法更加简洁。
List input = Arrays.asList(new String[] {"apple", "orange", "pear"});input.forEach((v) -> System.out.println(v));input.forEach(System.out::println);构造方法可以通过名称“new”来进行引用,如下面的代码所示:
List dateValues = Arrays.asList(new Long[] {0L, 1000L});List dates = CollectionUtils.map(dateValues, Date::new);