深入理解Java8雙冒號::的使用
一、方法引用
java8允許我們使用lambda表達(dá)式創(chuàng)建匿名方法。但有時(shí)lambda表達(dá)式除了調(diào)用現(xiàn)有方法之外什么也不做。在這些情況下,通過名稱引用現(xiàn)有的方法,通常能更直白的表現(xiàn)出方法的調(diào)用過程。對于已經(jīng)存在的且具有方法名稱的方法,它其實(shí)是簡潔且易于讀取的一種lambda表達(dá)式,或者說是對lambda表達(dá)式的一種進(jìn)一步簡化。
現(xiàn)在我們來看看下面這個“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); }}
假設(shè)你的社交網(wǎng)絡(luò)應(yīng)用程序的成員包含在一個數(shù)組中,并且你希望按年齡對數(shù)組進(jìn)行排序。你可以使用以下代碼:
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表達(dá)式,而不是定義并創(chuàng)建實(shí)現(xiàn)comparator的類的新實(shí)例,所以上上面的代碼可以用lambda表達(dá)式改寫成下面這樣:
Arrays.sort(rosterAsArray, (Person a, Person b) -> { return a.getBirthday().compareTo(b.getBirthday()); });
但是,我們已經(jīng)在Person bean對象中提前寫好了用來比較兩個person對象的出生日期的方法:,所以你其實(shí)可以在lambda表達(dá)式的主體中直接調(diào)用這個方法方法:
Arrays.sort(rosterAsArray, (Person a, Person b) -> { return a.getBirthday().compareTo(b.getBirthday()); } );
因?yàn)檫@個上面這個lambda表達(dá)式是在調(diào)用現(xiàn)有的方法,所以我們這里就可以使用上面提到的使用方法引用方式(及雙冒號 ::),而不是之前我們熟悉的lambda表達(dá)式:
Arrays.sort(rosterAsArray, Person::compareByAge);
方法引用 person::comparebyage 在語義上與lambda表達(dá)式(a,b)->person.comparebyage(a,b)相同。他們都有以下特點(diǎn):
- 它的形參列表復(fù)制自comparator<person>.compare,即
(Person, Person)
。 - 它的主體調(diào)用方法是:person.comparebyage。
二、方法引用的種類(哪些場景可以使用方法引用)
有四種方法引用:
種類 |
案例 |
---|---|
引用靜態(tài)方法 | ContainingClass::staticMethodName |
對特定對象的實(shí)例方法的引用 | containingObject::instanceMethodName |
對特定類型的任意對象的實(shí)例方法的引用 | ContainingType::methodName |
對構(gòu)造函數(shù)的引用 | ClassName::new |
1、引用靜態(tài)方法
例如:
Person::comparebyage 是對Person類的靜態(tài)方法 comparebyage 的引用。
2、引用特定對象的實(shí)例方法
以下是對特定對象的實(shí)例方法的引用示例:
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
調(diào)用對象myComparisonProvider的一部分方法
compareByName
。然后JRE會自動推斷方法類型參數(shù),在這種情況下為(Person, Person)
。
3、引用特定類型的任意對象的實(shí)例方法
以下是對特定類型的任意對象的實(shí)例方法的引用示例:
String[] stringArray = { "Barbara", "James", "Mary", "John", "Patricia", "Robert", "Michael", "Linda" }; Arrays.sort(stringArray, String::compareToIgnoreCase);
方法引用 string::compareTogignoreCase 的等效lambda表達(dá)式會有形參列表(String a,String b),其中a和b是用于更好地描述此示例的任意名稱。這次的方法引用,將調(diào)用方法a.CompareTognoreCase(b)。
這里官網(wǎng)原文可能說的不是很清楚,也有點(diǎn)拗口,多說一句:
如何理解:對特定類型的 任意對象的 實(shí)例方法的引用 這句話呢?
實(shí)際上上面代碼沒有在特定實(shí)例(也就是沒有new一個指定的String 實(shí)例出來,像這樣:String s = new String() ,s就是一個指定的String對象的實(shí)例)上引用方法 compareTogignoreCase ,而是在String類自身引用的。
我們進(jìn)入Arrays.sort(xx,xx)方法源碼,我們可以看到這個數(shù)組排序方法底層算法涉及循環(huán),那么這行代碼我們可以想象它會在每循環(huán)到數(shù)組的一個String元素時(shí),以這個String元素所屬類型對象(其實(shí)就是String對象)的身份去調(diào)用compareTogignoreCase方法,即:
會調(diào)用
的compareTogignoreCase --》String Barbara = new String(); Barbara.compareTogignoreCase
會調(diào)用
的compareTogignoreCase --》String James= new String();James.compareTogignoreCase
以此類推。。。
我可能解釋的也不太好,主要就是想語言上理解一下:對特定類型的 任意對象的 實(shí)例方法 的引用
4、引用構(gòu)造函數(shù)
你可以通過使用name new 的方式引用構(gòu)造函數(shù),這與靜態(tài)方法的引用方式類似。
下面這個示例方法,是將元素從一個集合復(fù)制到另一個集合。我們將以這個方法為例,講解如何使用方法引用的方式引用構(gòu)造函數(shù)。
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; }
函數(shù)接口supplier包含一個無參但返回一個對象的get方法,源碼可見,他返回的類型就是泛型的類型,如下:
因此,可以使用lambda表達(dá)式調(diào)用方法transferElements,如下所示:
Set<Person> rosterSetLambda = transferElements(roster, () -> { return new HashSet<>(); });
可以使用構(gòu)造函數(shù)引用代替lambda表達(dá)式,如下所示:
Set<Person> rosterSet = transferElements(roster, HashSet::new);
上面代碼,Java編譯器執(zhí)行的時(shí)候會自動推斷出這里想要創(chuàng)建一個包含Person類型元素的hashset集合。當(dāng)然你也可以顯示的指定類型,如下:
Set<Person> rosterSet = transferElements(roster, HashSet<Person>::new);
到此這篇關(guān)于深入理解Java8雙冒號::的使用的文章就介紹到這了,更多相關(guān)Java8雙冒號::內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
@Transactional遇到try catch失效的問題
這篇文章主要介紹了@Transactional遇到try catch失效的問題及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01@PostConstruct在項(xiàng)目啟動時(shí)被執(zhí)行兩次或多次的原因及分析
這篇文章主要介紹了@PostConstruct在項(xiàng)目啟動時(shí)被執(zhí)行兩次或多次的原因及分析,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08對數(shù)據(jù)進(jìn)行分頁顯示到table中的實(shí)現(xiàn)方法
這篇文章主要介紹了對數(shù)據(jù)進(jìn)行分頁顯示到table中的實(shí)現(xiàn)方法的相關(guān)資料,非常不錯,具有參考借鑒價(jià)值,需要的朋友可以參考下2016-05-05Spring Boot集成Sorl搜索客戶端的實(shí)現(xiàn)代碼
本篇文章主要介紹了Spring Boot集成Sorl搜索客戶端的實(shí)現(xiàn)代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-11-11JAVA設(shè)計(jì)模式之備忘錄模式原理與用法詳解
這篇文章主要介紹了JAVA設(shè)計(jì)模式之備忘錄模式,簡單說明了備忘錄模式的概念、原理并結(jié)合實(shí)例形式分析了java備忘錄模式的具體定義及使用方法,需要的朋友可以參考下2017-08-08