你可以使用 lambda 表达式来创建匿名方法,然而,有时候 lambda 表达式只是用来调用一个已存在的方法。这种情况下,直接调用方法的名字会更简单直接。方法引用就是干这种事的。
简介
用下面的 Person 类来做一个说明:
1 | public class Person { |
假设服务器返回给你的是一个数组,然后你希望根据 age 来排序,你就可能会写出如下的代码:
1 | Person[] rosterAsArray = roster.toArray(new Person[roster.size()]); |
调用排序的方法签名如下:
1 | static <T> void sort (T[] a, Comparator<? super T> c) |
注意到 Comparator 接口是一个方法接口,因此,你可以使用 lambda 表达式来定义并创建一个实现了 Comparator 接口的对象:
1 | Arrays.sort(rosterAsArray, (Person a, Person b) -> { |
并且,这个比较生日大小的方法已经存在了,就是 Person.compareByAge。你可以在 lambda 表达式中调用这个方法:
1 | Arrays.sort(rosterAsArray, (a, b) -> Person.compareByAge(a, b)); |
由于 lambda 表达式只是调用了一个已存在的方法,你可以使用方法引用来代替 lambda 表达式:
1 | Arrays.sort(rosterAsArray, Person::compareByAge); |
其中,Person::compareByAge 在语义上等同于 lambda 表达式 (a, b) -> Person.compareByAge(a, b)。两者都有如下的特征:
- 参数列表和 Comparator
.compare 一样,都是 (Person, Person)。 - 都是调用同一个方法:Person.compareByAge。
方法引用的类型
方法引用一共有四种类型:
类型 | 举例 |
---|---|
引用静态方法 | ContainingClass::staticMethodName |
引用某个对象的实例方法 | containingObject::instanceMethodName |
引用任意内置类的实例方法 | ContainingType::methodName |
引用构造方法 | ClassName::new |
下面分别介绍着四种类型:
- 引用静态方法
前面的 Person::compareByAge 就是一个静态方法引用。
- 引用某个对象的实例方法
用下面这个例子做说明:
1 | class ComparsionProvider { |
myComparisonProvider::compareByName 这个方法引用调用了 myComparisonProvider 对象的一个 compareByName 方法。JRE 可以推测方法的参数类型,比如在这个例子中就是 (Person, Person)。
- 引用任意内置类的实例方法
用下面这个例子做说明:
1 | String[] stringArray = {"Barbara", "James", "Mary", "John", "Patricia", "Robert"}; |
和方法引用 String::compareToIgnoreCase 等价的 lambda 表达式会有一个参数列表 (String a, String b),而方法引用会直接调用方法 a.compareToIgnoreCase(b)。
- 引用构造方法
和引用静态方法类似,你只需要调用 new 这个方法就行了,下面举例说明:
1 | public static <T, SOURCE extends Collection<T>, DEST extends Collection<T>> DEST transferElements (SOURCE sourceCollection, Supplier<DEST> collectionFactory) [ |
你可以像下面那样使用 lambda 调用 transferElements 方法:
1 | Set<Person> rosterSetLambda = transferElements(roster, () -> {return new HashSet<>();}); |
也可以使用方法引用:
1 | Set<Person> rosterSet = transferElements(roster, HashSet::new); |
Java 编译器会推测出你想要创建一个 HashSet 集合来存储 Person 对象,当然,你也可以使用下面的方法来指明对象类型:
1 | Set<Person> rosterSet = transferElements(roster, HashSet<Person>::new); |