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