译者注:本篇博客翻译自Oracle官方教程《Method References》。作为Java 8 新特性Lambda表达式的引申概念,博主依然采用官方文档的方式来学习这一重要的概念。希望对各位同道有所帮助。
使用Lambda表达式创建匿名方法。但是,有时候Lambda表达式什么都没做,仅仅是调用了一个已经存在的方法。这种情况下,引用已存在方法的方法名通常是更清晰的。方法引用允许你这么做,它是一种简洁的、可读性强的有名方法的Lambda表达式。
再次思考我们在Lambda 表达式部分(即上一篇翻译《Java8————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);
}}
假设你的应用中的成员全都包含在一个数组中,并且你希望把他们以年龄进行排序。你可以使用下面的代码:
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());
}
);
但是,比较两个Person对象生日的方法已经存在于Person.compareByAge方法当中。你可以Lambda表达式的body中调用:
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) 是一样的。都具备以下特性:
1、它的参数列表:(Person , Person)从Comparator
2、它的主体调用方法Person.compareByAge()
有如下四种方法引用:
种类 | 举例 |
---|---|
Reference to a static method | ContainingClass::staticMethodName |
Reference to an instance method of a particular object | containingObject::instanceMethodName |
Reference to an instance method of an arbitrary object of a particular type | ContainingType::methodName |
Reference to a constructor | 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);
与Lambda表达式等价的方法引用 String::compareToIgnoreCase 有一个正式的参数列表:(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);