声明:本文翻译自The Java™ Tutorials(官方文档)
方法引用
你使用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 {
public int compare(Person a, Person b) {
return a.getBirthday().compareTo(b.getBirthday());
}
}
Arrays.sort(rosterAsArray, new PersonAgeComparator());
这个sort
调用的方法签名如下:
static void sort(T[] a, Comparator super T> c)
注意Comparator
一个函数式接口。因此你可以使用Lambda表达式代替创建一个新的类实例来实现Comparator
接口:
Arrays.sort(rosterAsArray,
(Person a, Person b) -> {
return a.getBirthday().compareTo(b.getBirthday());
}
);
由于这个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
调用的方法compareByName
是myComparisonProvider
对象的一部分。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 , DEST extends Collection>
DEST transferElements(
SOURCE sourceCollection,
Supplier collectionFactory) {
DEST result = collectionFactory.get();
for (T t : sourceCollection) {
result.add(t);
}
return result;
}
函数式接口Supplier
包含一个没有参数,但返回一个对象的get
方法。因此你可以使用Lambda表达式调用transferElements
方法就像下面这样:
Set rosterSetLambda =
transferElements(roster, () -> { return new HashSet<>(); });
你可以像下面这样使用构造函数引用代替Lambda表达式:
Set rosterSet = transferElements(roster, HashSet::new);
Java编译器推断你想创建一个包含Person
类型的HashSet
集合,其实你也可以指定类型,比如:
Set rosterSet = transferElements(roster, HashSet::new);