方法引用
原文: https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html
您使用 lambda 表达式来创建匿名方法。但是,有时,lambda 表达式只会调用现有方法。在这些情况下,通过名称引用现有方法通常更清楚。方法引用使您可以这样做;对于已有名称的方法,它们是紧凑的,易于阅读的 lambda 表达式。
再次考虑 Lambda 表达式一节中讨论的 Person 类:
public class Person {public enum Sex {MALE, FEMALE}String name;LocalDate birthday;Sex gender;String emailAddress;public int getAge() {// ...}public Calendar getBirthday() {return birthday;}public static int compareByAge(Person a, Person b) {return a.birthday.compareTo(b.birthday);}}
假设您的社交网络应用程序的成员包含在一个数组中,并且您希望按年龄对数组进行排序。您可以使用以下代码(在示例 MethodReferencesTest 中找到本节中描述的代码摘录):
Person[] rosterAsArray = roster.toArray(new Person[roster.size()]);class PersonAgeComparator implements Comparator<Person> {public int compare(Person a, Person b) {return a.getBirthday().compareTo(b.getBirthday());}}Arrays.sort(rosterAsArray, new PersonAgeComparator());
sort调用的方法签名如下:
static <T> void sort(T[] a, Comparator<? super T> c)
请注意,接口Comparator是一个函数式接口。因此,您可以使用 lambda 表达式而不是定义然后创建实现Comparator的类的新实例:
Arrays.sort(rosterAsArray,(Person a, Person b) -> {return a.getBirthday().compareTo(b.getBirthday());});
但是,这种比较两个Person实例的出生日期的方法已经存在Person.compareByAge。您可以在 lambda 表达式的主体中调用此方法:
Arrays.sort(rosterAsArray,(a, b) -> Person.compareByAge(a, b));
因为此 lambda 表达式调用现有方法,所以可以使用方法引用而不是 lambda 表达式:
Arrays.sort(rosterAsArray, Person::compareByAge);
方法引用Person::compareByAge在语义上与 lambda 表达式(a, b) -> Person.compareByAge(a, b)相同。每个都有以下特点:
- 其形式参数列表从
Comparator<Person>.compare复制,即(Person, Person)。 - 它的主体调用方法
Person.compareByAge。
各种方法引用
有四种方法引用:
| 类 | 例 |
|---|---|
| 引用静态方法 | ContainingClass::staticMethodName |
| 引用特定对象的实例方法 | containingObject::instanceMethodName |
| 引用特定类型的任意对象的实例方法 | ContainingType::methodName |
| 引用构造器 | ClassName::new |
方法引用Person::compareByAge是对静态方法的引用。
以下是对特定对象的实例方法的引用示例:
class ComparisonProvider {public int compareByName(Person a, Person b) {return a.getName().compareTo(b.getName());}public int compareByAge(Person a, Person b) {return a.getBirthday().compareTo(b.getBirthday());}}ComparisonProvider myComparisonProvider = new ComparisonProvider();Arrays.sort(rosterAsArray, myComparisonProvider::compareByName);
方法引用myComparisonProvider::compareByName调用作为对象myComparisonProvider一部分的方法compareByName。 JRE 推断出方法类型参数,在本例中是(Person, Person)。
以下是对特定类型的任意对象的实例方法的引用示例:
String[] stringArray = { "Barbara", "James", "Mary", "John","Patricia", "Robert", "Michael", "Linda" };Arrays.sort(stringArray, String::compareToIgnoreCase);
方法引用String::compareToIgnoreCase的等效 lambda 表达式将具有形式参数列表(String a, String b),其中a和b是用于更好地描述此示例的任意名称。方法引用将调用方法a.compareToIgnoreCase(b)。
您可以使用名称new以与静态方法相同的方式引用构造器。以下方法将元素从一个集合复制到另一个集合:
public static <T, SOURCE extends Collection<T>, DEST extends Collection<T>>DEST transferElements(SOURCE sourceCollection,Supplier<DEST> collectionFactory) {DEST result = collectionFactory.get();for (T t : sourceCollection) {result.add(t);}return result;}
函数式接口Supplier包含一个不带参数并返回对象的方法get。因此,您可以使用 lambda 表达式调用方法transferElements,如下所示:
Set<Person> rosterSetLambda =transferElements(roster, () -> { return new HashSet<>(); });
您可以使用构造器引用代替 lambda 表达式,如下所示:
Set<Person> rosterSet = transferElements(roster, HashSet::new);
Java 编译器推断您要创建包含Person类型元素的HashSet集合。或者,您可以按如下方式指定:
Set<Person> rosterSet = transferElements(roster, HashSet<Person>::new);
