您好,登錄后才能下訂單哦!
這篇文章主要介紹“java8的Lambda表達(dá)式怎么應(yīng)用”的相關(guān)知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“java8的Lambda表達(dá)式怎么應(yīng)用”文章能幫助大家解決問題。
前片文章講到,使用匿名類來表示不同的行為并不令人滿意:代碼十分啰嗦,這會影響程序
員在實踐中使用行為參數(shù)化的積極性。在本章中,我們會教給你Java 8中解決這個問題的新工
具——Lambda表達(dá)式。它可以讓你很簡潔地表示一個行為或傳遞代碼?,F(xiàn)在你可以把Lambda
表達(dá)式看作匿名功能,它基本上就是沒有聲明名稱的方法,但和匿名類一樣,它也可以作為參
數(shù)傳遞給一個方法。
可以吧Lambda表達(dá)式簡單的理解為可以可以傳遞匿名函數(shù)的一種形式:它沒有名稱,但是有參數(shù)列表,函數(shù)主體,返回類型,甚至還可以有一個可以拋出異常的函數(shù)列表.
匿名:我們說匿名,是因為它不像普通的方法那樣有一個明確的名稱:寫得少而想得多!
函數(shù): 我們說它是函數(shù),是因為Lambda函數(shù)不像方法那樣屬于某個特定的類。但和方法一樣,Lambda有參數(shù)列表、函數(shù)主體、返回類型,還可能有可以拋出的異常列表。
傳遞:Lambda表達(dá)式可以作為參數(shù)傳遞給方法或存儲在變量中.
簡潔:無需像匿名類那樣寫很多模版代碼.
在前面的講解中,我們寫過一個簡單的Lambda表達(dá)式
可以看到,Lambda表達(dá)式有三個部分:
* 參數(shù)列表:這里它采用了 Comparator 中 compare 方法的參數(shù),兩個 Apple 。
* 箭頭:把參數(shù)和函數(shù)主題分開.
* Lambda主體: 比較兩個 Apple 的重量。表達(dá)式就是Lambda的返回值了。
為了進(jìn)一步說明,下面給出了Java 8中五個有效的Lambda表達(dá)式的例子。
java語言設(shè)計者選擇這樣的語法,是因為C#和Scala等語言中的類似功能廣受歡迎.Lambda的基本語法就是:
(parameters) -> expression
或(請注意語句的花括號)
(parameters) -> { statements; }
一言以蔽之,函數(shù)式接口就是只定義一個抽象方法的接口。
如我們前面創(chuàng)建的:
public interface Predicate<T>{ boolean
函數(shù)式接口的抽象方法的簽名基本上就是Lambda表達(dá)式的簽名,我們將這種抽象方法叫做函數(shù)描述符.
這很好理解:因為函數(shù)式接口只有一個抽象方法,因此我們只需要知道參數(shù)列表,和返回值就可以描述這個函數(shù).
例如, Runnable 接口可以看作一個什么也不接受什么也不返回( void )的函數(shù)的
簽名,因為它只有一個叫作 run 的抽象方法,這個方法什么也不接受,什么也不返回( void )。
函數(shù)式接口定義且只定義了一個抽象方法.函數(shù)式接口很有用,因為抽象方法的簽名可以描述為Lambda表達(dá)式的簽名
函數(shù)式接口的抽象方法的簽名稱為函數(shù)描述符.
Java API中已經(jīng)有了幾個函數(shù)式接口
Java 8的庫設(shè)計師幫你在 java.util.function 包中引入了幾個新的函數(shù)式接口。我們接下
來會介紹 Predicate 、 Consumer 和 Function ,更完整的可以查看API.
為了總結(jié)關(guān)于函數(shù)式接口和Lambda的討論,表3-3總結(jié)了一些使用案例、Lambda的例子,以
及可以使用的函數(shù)式接口。
Lambda表達(dá)式的類型是從Lambda的上下文中推斷出來的,上下文中Lambda表達(dá)式需要的類型稱為目標(biāo)類型.
舉個上節(jié)中的例子:
java編譯器可以從上下文中推斷出用什么函數(shù)式接口來配合Lambda表達(dá)式,這意味著他可以推斷出適合Lambda表達(dá)式的簽名,因為函數(shù)描述符也可以從目標(biāo)類型中得到.
這樣做的好處在于,編譯器可以了解Lambda表達(dá)式的參數(shù)類型
Lambda表達(dá)式有多個參數(shù),代碼可讀性的好處就更為明顯。例如,你可以這樣來創(chuàng)建一個
Comparator 對象:
請注意,有時候顯式寫出類型更易讀,有時候去掉它們更易讀。沒有什么法則說哪種更好;
對于如何讓代碼更易讀,程序員必須做出自己的選擇
Lambda表達(dá)式也允許使用自有變量(不是參數(shù),是在外層作用域定義的變量),就想匿名類一樣,他們被稱為捕獲Lambda
int portNumber = 1337; Runnable r = () -> System.out.println(portNumber);
需要注意的是:盡管Lambda可以沒有限制地捕獲(也就是在其主體中引用)實例變量和靜態(tài)變量。但是局部變量必須顯示聲明為final,或者事實上就是final.
換句話說:Lambda表達(dá)式只能捕獲指派給他們的局部變量一次((注:捕獲實例變量可以被看作捕獲最終局部變量 this 。)),
例如,下面的代碼無法編譯,因為 portNumber
變量被賦值兩次:
你可能會問自己,為什么局部變量有這些限制?????
第一,實例變量和局部變量背后的實現(xiàn)有一個關(guān)鍵的不同,實例變量存儲在堆中,局部變量存儲在棧中,
如果Lambda可以直接訪問局
部變量,而且Lambda是在一個線程中使用的,則使用Lambda的線程,可能會在分配該變量的線
程將這個變量收回之后,去訪問該變量。因此,Java在訪問自由局部變量時,實際上是在訪問它
的副本,而不是訪問原始變量。如果局部變量僅僅賦值一次那就沒有什么區(qū)別了——因此就有了
這個限制。
第二,這一限制不鼓勵你使用改變外部變量的典型命令式編程模式(我們會在以后的各章中
解釋,這種模式會阻礙很容易做到的并行處理)
閉包:
你可能已經(jīng)聽說過閉包這個詞,你可能會想Lambda是否滿足閉包的定義.科學(xué)的講,閉包就是一個函數(shù)的實例.且它可以無限制的訪問那個函數(shù)的非本地變量.
例如,閉包可以作為參數(shù)傳遞給另一個函數(shù)。它也可以訪
問和修改其作用域之外的變量?,F(xiàn)在,Java 8的Lambda和匿名類可以做類似于閉包的事情:它們可以作為參數(shù)傳遞給方法,并且可以訪問其作用域之外的變量.
但是有一個限制:就是它們不能修改定義Lambda的方法的局部變量的內(nèi)容.這些變量必須是隱式最終的.可以認(rèn)為Lambda是對值封閉,而不是對變量封閉.
如前所述: 這種限制的原因在于局部變量保存在棧上,并且隱式表示它們僅限于其所在線程.如果允許捕獲局部變量,就會引發(fā)造成線程不安全的新得可能性,而這時我們不想看到的
實例變量是可以的,因為它們保存在堆中,而堆是在線程中共享的.
方法引用可以被看作是調(diào)用Lambda表達(dá)式的一種快捷方法.
它的基本思想是:
如果一個Lambda表達(dá)式代表是 直接調(diào)用這個方法,那最好還是用名稱來調(diào)用它.
事實上,方法引用就是讓你根據(jù)已有的方法來創(chuàng)建Lambda表達(dá)式.
它是如何工作的呢?
當(dāng)你需要使用方法引用時,目標(biāo)引用放在分隔符 :: 前,方法名放在后面.
例如,
Apple::getWeight 就是引用了 Apple 類中定義的方法 getWeight 。請記住,不需要括號,因為
你沒有實際調(diào)用這個方法。方法引用就是Lambda表達(dá)式 (Apple a) -> a.getWeight() 的快捷
寫法。
/** * 方法引用 */ @Test public void test5(){ List<Apple> inventory1 = initInventory(); inventory1.sort((Apple a1, Apple a2)-> a1.getWeight().compareTo(a2.getWeight())); System.out.println(inventory1); List<Apple> inventory = initInventory(); inventory.sort(Comparator.comparing(Apple::getWeight)); System.out.println(inventory); }
現(xiàn)在讓我們用一個上節(jié)中對蘋果排序的例子貫穿一下我們目前接觸到的所有知識
我們想要實現(xiàn)的最終解決方案是這樣的:
inventory.sort(comparing(Apple::getWeight));
Java 8的API已經(jīng)為你提供了一個 List 可用的 sort 方法,你不用自己去實現(xiàn)它。
那么最困難的部分已經(jīng)搞定了!但是,如何把排序策略傳遞給 sort 方法呢?你看, sort 方法的
簽名是這樣的:
void sort(Comparator<? super
它需要一個Comparator對象來比較兩個Apple,這就是zaijava中傳遞策略的方式:他們必須包裹在一個對象里,
我們說 sort 的行為被參數(shù)化了:傳遞給它的排序策略不同,其行為也會不同.
你的第一個解決方案看上去是這樣的:
public class AppleComparator implements Comparator<Apple> { public int compare(Apple a1, Apple a2){ return a1.getWeight().compareTo(a2.getWeight()); } } inventory.sort(new
你可以使用匿名類來改進(jìn)解決方案,而不是實現(xiàn)一個 Comparator 卻只實
例化一次:
inventory.sort(new Comparator<Apple>() { public int compare(Apple a1, Apple a2){ return
但你的解決方案仍然挺啰嗦的。Java 8引入了Lambda表達(dá)式,它提供了一種輕量級語法來實
現(xiàn)相同的目標(biāo):傳遞代碼
inventory.sort((Apple a1, Apple a2)-> a1.getWeight().compareTo(a2.getWeight()));
我們前面解釋過了,Java編譯器可以根據(jù)Lambda出現(xiàn)的上下文來推斷Lambda表達(dá)式參數(shù)的
類型。那么你的解決方案就可以重寫成這樣:
inventory.sort((a1, a2) -> a1.getWeight().compareTo(a2.getWeight()));
你的代碼還能變得更易讀一點嗎???
Comparator 具有一個叫做Comparing的靜態(tài)輔助方法,方法描述如下:
public static <T, U extends Comparable<? super U>> Comparator<T> comparing( Function<? super T, ? extends U> keyExtractor) { Objects.requireNonNull(keyExtractor); return
可以看到,它可以接受一個Function來提取一個鍵值,并生成一個Compartor對象.
它可以像下面這樣用(注意你現(xiàn)在傳遞的Lambda只有一
個參數(shù):Lambda說明了如何從蘋果中提取需要比較的鍵值):
Comparator<Apple> c = Comparator.comparing((Apple a) -> a.getWeight());
因此,現(xiàn)在你可以該代碼改為這樣:
import static
方法引用就是替代那些轉(zhuǎn)發(fā)參數(shù)的Lambda表達(dá)式的語法糖.
你可以使用方法引用讓你的代碼更簡潔,假設(shè)你靜態(tài)導(dǎo)入了 java.util.Comparator.comparing.
那么你現(xiàn)在的代碼可以這樣:
inventory.sort(comparing(Apple::getWeight));
關(guān)于“java8的Lambda表達(dá)式怎么應(yīng)用”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識,可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會為大家更新不同的知識點。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。