Java Lambda从入门到精通六 方法引用

如果我们要对苹果集合按照重量进行排序,使用我们前面已学的lambda要怎么写呢?

苹果集合

List<Apple> apples = new ArrayList<>();
        Apple a1 = new Apple("红",155);
        Apple a2 = new Apple("绿",136);
        Apple a3 = new Apple("红",169);
        apples.add(a1);
        apples.add(a2);
        apples.add(a3);

要对这个苹果集合按照重量由轻到重排序,如何写?

首先要知道List本身提供了sort方法,方法需要传入一个Comparator的实现,用做排序逻辑,我们写一个lambda就可以了,不用再像以前写一个实现类或者匿名类。

System.out.println("---排序前---");
apples.forEach(System.out::println);
apples.sort((Apple a11, Apple a22) -> a11.getWeight() <= a22.getWeight()?-1:1);
System.out.println("---排序后---");
apples.forEach(System.out::println);

输出结果:

---排序前---
Apple(color=红, weight=155)
Apple(color=绿, weight=136)
Apple(color=红, weight=169)
---排序后---
Apple(color=绿, weight=136)
Apple(color=红, weight=155)
Apple(color=红, weight=169)

这是我们运用前面几节学到的知识,写出的lambda表达式实现了排序功能。

在Java8中,一个新特性就是可以直接引用一个类的普通方法,这种用法叫做:方法引用

运用方法引用,我们将上面的排序进行一些改造:

System.out.println("---排序前---");
apples.forEach(System.out::println);
apples.sort(Comparator.comparing(Apple::getWeight).reversed());
System.out.println("---排序后---");
apples.forEach(System.out::println);

输出结果:

---排序前---
Apple(color=绿, weight=136)
Apple(color=红, weight=155)
Apple(color=红, weight=169)
---排序后---
Apple(color=红, weight=169)
Apple(color=红, weight=155)
Apple(color=绿, weight=136)

一个类中的普通成员方法,使用方法引用的方式调用写入是:

类名::方法名

类名和方法名中间由两个冒号。

上面的例子中,Comparator.comparing(Apple::getWeight),使用了Comparator类提供的comparing方法,方法参数是:Apple::getWeight,是不是有点不可思议,先看一下comparing方法的源码;

public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
            Function<? super T, ? extends U> keyExtractor)
    {
        Objects.requireNonNull(keyExtractor);
        return (Comparator<T> & Serializable)
            (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
    }

我们可以看到最重要的一行:

(c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2))

其实这还是我们第一种写法,只不过,通过方法引用,进行了简化,相当于一个语法糖,在源码中,还是需要两个参数,来进行比较,而参数keyExtractor,则可以转化为:c1.getWeight().compareTo.c2.getWeight()。

源码中的:

keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2))

可以转化为:

c1.getWeight().compareTo.c2.getWeight()

其实就是这样的效果,这也就是我们前面所说的:行为参数化。我们的参数是一个getWeight()的行为,而不是像传统方法,c1.getWeight()=155,拿这个155当作参数,传入方法。

在本文的第二个例子中:

Comparator.comparing(Apple::getWeight).reversed()

我们最后还用到了reversed(),相当于是对排序结果反转。

方法引用,并不仅仅是上面这一种使用方法,主要由以下几种:

实例上的实例方法引用instanceReference::methodName
父类实例方法引用super::methodName
类型上的实例方法引用ClassName::methodName
构造方法引用Class::new
数组构造方法引用TypeName[]:new
静态方法ClassName::methodName
lambda方法引用的几种方式