Java8————方法引用
譯者注:本篇部落格翻譯自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<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表示式的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<Person>.compare複製過來。
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 <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);