3.7 Lambda和方法引用实战

为了给这一章还有我们讨论的所有关于Lambda的内容收个尾,我们需要继续研究开始的那个问题——用不同的排序策略给一个Apple列表排序,并需要展示如何把一个原始粗暴的解决方案转变得更为简明。这会用到书中迄今讲到的所有概念和功能:行为参数化、匿名类、Lambda表达式和方法引用。我们想要实现的最终解决方案是这样的:

  1. inventory.sort(comparing(Apple::getWeight));

3.7.1 第1步:传递代码

你很幸运,Java 8 API已经为你提供了一个List可用的sort方法,你不用自己去实现它。那么最困难的部分已经搞定了!但是,如何把排序策略传递给sort方法呢?你看,sort方法的签名是这样的:

  1. void sort(Comparator<? super E> c)

它需要一个Comparator对象来比较两个Apple!这就是在Java中传递策略的方式:它们必须包裹在一个对象里。我们说sort行为参数化了:传递给它的排序策略不同,其行为也会不同。

你的第一个解决方案看上去是这样的:

  1. public class AppleComparator implements Comparator<Apple> {
  2. public int compare(Apple a1, Apple a2){
  3. return a1.getWeight().compareTo(a2.getWeight());
  4. }
  5. }
  6. inventory.sort(new AppleComparator());

3.7.2 第2步:使用匿名类

你在前面看到了,你可以使用匿名类来改进解决方案,而不是实现一个Comparator却只实例化一次:

  1. inventory.sort(new Comparator<Apple>() {
  2. public int compare(Apple a1, Apple a2){
  3. return a1.getWeight().compareTo(a2.getWeight());
  4. }
  5. });

3.7.3 第3步:使用Lambda表达式

但你的解决方案仍然挺啰唆的。Java 8引入了Lambda表达式,它提供了一种轻量级语法来实现相同的目标:传递代码。你看到了,在需要函数式接口的地方可以使用Lambda表达式。回顾一下:函数式接口就是仅仅定义一个抽象方法的接口。抽象方法的签名(称为函数描述符)描述了Lambda表达式的签名。在这个例子里,Comparator代表了函数描述符(T, T) -> int。因为你用的是苹果,所以它具体代表的就是(Apple, Apple) -> int。改进后的新解决方案看上去就是这样的了:

  1. inventory.sort((Apple a1, Apple a2)
  2. -> a1.getWeight().compareTo(a2.getWeight())
  3. );

前面解释过了,Java编译器可以根据Lambda出现的上下文来推断Lambda表达式参数的类型。那么你的解决方案就可以重写成这样:

  1. inventory.sort((a1, a2) -> a1.getWeight().compareTo(a2.getWeight()));

你的代码还能变得更易读一点吗?Comparator具有一个叫作comparing的静态辅助方法,它可以接受一个Function来提取Comparable键值,并生成一个Comparator对象(第13章会解释为什么接口可以有静态方法)。它可以像下面这样用(注意你现在传递的Lambda只有一个参数,Lambda说明了如何从Apple中提取需要比较的键值):

  1. Comparator<Apple> c = Comparator.comparing((Apple a) -> a.getWeight());

现在你可以把代码再改得紧凑一点了:

  1. import static java.util.Comparator.comparing;
  2. inventory.sort(comparing(apple -> apple.getWeight()));

3.7.4 第4步:使用方法引用

前面解释过,方法引用就是替代那些转发参数的Lambda表达式的语法糖。你可以用方法引用让你的代码更简洁(假设你静态导入了java.util.Comparator.comparing):

  1. inventory.sort(comparing(Apple::getWeight));

恭喜你,这就是你的最终解决方案!这比Java 8之前的代码好在哪儿呢?它比较短;它的意思也很明显,并且代码读起来和问题描述差不多:“对库存进行排序,比较苹果的重量。”