一、方法引用
java8允許我們使用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);
}}
假設你的社交網絡應用程序的成員包含在一個數組中,並且你希望按年齡對數組進行排序。你可以使用以下代碼:
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的類的新實例,所以上上面的代碼可以用lambda表達式改寫成下面這樣:
Arrays.sort(rosterAsArray,
(Person a, Person b) -> {
return a.getBirthday().compareTo(b.getBirthday());
}
);
但是,我們已經在Person bean對象中提前寫好了用來比較兩個person對象的出生日期的方法:,所以你其實可以在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 |
1、引用靜態方法
例如:
Person::comparebyage 是對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)
。
3、引用特定類型的任意對象的實例方法
以下是對特定類型的任意對象的實例方法的引用示例:
String[] stringArray = {
"Barbara", "James", "Mary", "John",
"Patricia", "Robert", "Michael", "Linda"
};
Arrays.sort(stringArray, String::compareToIgnoreCase);
方法引用 string::compareTogignoreCase 的等效lambda表達式會有形參列表(String a,String b),其中a和b是用於更好地描述此示例的任意名稱。這次的方法引用,將調用方法a.CompareTognoreCase(b)。
這裏官網原文可能說的不是很清楚,也有點拗口,多說一句:
如何理解:對特定類型的 任意對象的 實例方法的引用 這句話呢?
實際上上面代碼沒有在特定實例(也就是沒有new一個指定的String 實例出來,像這樣:String s = new String() ,s就是一個指定的String對象的實例)上引用方法 compareTogignoreCase ,而是在String類自身引用的。
我們進入Arrays.sort(xx,xx)方法源碼,我們可以看到這個數組排序方法底層算法涉及循環,那麼這行代碼我們可以想象它會在每循環到數組的一個String元素時,以這個String元素所屬類型對象(其實就是String對象)的身份去調用compareTogignoreCase方法,即:
會調用的compareTogignoreCase --》String Barbara = new String(); Barbara.compareTogignoreCase
會調用的compareTogignoreCase --》String James= new String();James.compareTogignoreCase
以此類推。。。
我可能解釋的也不太好,主要就是想語言上理解一下:對特定類型的 任意對象的 實例方法 的引用
4、引用構造函數
你可以通過使用name 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);
java8 新特性解析(更新中):
Java8新特性1:lambda表達式入門--由淺入深,從單發步槍邁向自動步槍
Java8新特性3:Stream1——什麼是Stream,Stream的特性,如何使用Stream,Stream與Collection集合的區別