激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

香港云服务器
服務(wù)器之家 - 編程語言 - JAVA教程 - Java函數(shù)式編程(五):閉包

Java函數(shù)式編程(五):閉包

2019-11-29 16:20deepinmind JAVA教程

這篇文章主要介紹了Java函數(shù)式編程(五):閉包,本文是系列文章的第5篇,其它篇章請(qǐng)參閱相關(guān)文章,需要的朋友可以參考下

使用詞法作用域和閉包

很多開發(fā)人員都存在這種誤解,認(rèn)為使用lambda表達(dá)式會(huì)導(dǎo)致代碼冗余,降低代碼質(zhì)量。恰恰相反,就算代碼變得再復(fù)雜,我們也不會(huì)為了代碼的簡(jiǎn)潔性而在代碼質(zhì)量上做任何妥協(xié),下面我們就會(huì)看到。

在前面一個(gè)例子中我們已經(jīng)可以重用lambda表達(dá)式了;然而,如果再匹配另外一個(gè)字母,代碼冗余的問題很快又卷土重來了。我們先來進(jìn)一步分析下這個(gè)問題,然后再用詞法作用域和閉包來把它解決掉。

lambda表達(dá)式帶來的冗余

我們來從friends中過濾出那些以N或者B開頭的字母。繼續(xù)延用上面的那個(gè)例子,我們寫出的代碼可能會(huì)是這樣的:

復(fù)制代碼代碼如下:

final Predicate<String> startsWithN = name -> name.startsWith("N");
final Predicate<String> startsWithB = name -> name.startsWith("B");
final long countFriendsStartN =
friends.stream()
.filter(startsWithN).count();
final long countFriendsStartB =
friends.stream()
.filter(startsWithB).count();

 

第一個(gè)predicate判斷名字是否是以N開頭的,而第二個(gè)是判斷是否以B開頭的。我們把這兩個(gè)實(shí)例分別傳遞給兩次filter方法調(diào)用。這樣看起來很合理,但是兩個(gè)predicate產(chǎn)生了冗余,它們只是那個(gè)檢查的字母不同而已。我們來看下如何能避免這種冗余。

使用詞法作用域來避免冗余

第一個(gè)方案,我們可以把字母抽出來作為函數(shù)的參數(shù),同時(shí)把這個(gè)函數(shù)傳遞給filter方法。這是個(gè)不錯(cuò)的方法,不過filter可不是什么函數(shù)都接受的。它只接受只有一個(gè)參數(shù)的函數(shù),那個(gè)參數(shù)對(duì)應(yīng)的就是集合中的元素,返回一個(gè)boolean值,它希望傳進(jìn)來的是一個(gè)Predicate。

我們希望有一個(gè)地方能把這個(gè)字母先緩存起來,一直到參數(shù)傳遞過來(在這里就是name這個(gè)參數(shù))。下面來新建一個(gè)這樣的函數(shù)。

 

復(fù)制代碼代碼如下:

public static Predicate<String> checkIfStartsWith(final String letter) { 
return name -> name.startsWith(letter);
}

 

我們定義了一個(gè)靜態(tài)函數(shù)checkIfStartsWith,它接收一個(gè)String參數(shù),并且返回一個(gè)Predicate對(duì)象,它正好可以傳遞給filter方法,以便后面進(jìn)行使用。不像前面看到的高階函數(shù)那樣是以函數(shù)作為參數(shù)的,這個(gè)方法返回的是一個(gè)函數(shù)。不過它也是一個(gè)高階函數(shù),這個(gè)我們?cè)?2頁的進(jìn)化,而非變革中已經(jīng)提到過了。

checkIfStartsWith方法返回的Predicate對(duì)象和其它lambda表達(dá)式有些不同。在 return name -> name.startsWith(letter)語句中,我們很清楚name是什么,它是傳入到lambda表達(dá)式中的參數(shù)。不過變量letter到底是什么?它是在這個(gè)匿名函數(shù)的域外邊的,Java找到了定義這個(gè)lambda表達(dá)式的域,并發(fā)現(xiàn)了這個(gè)變量letter。這個(gè)就叫做詞法作用域。詞法作用域是個(gè)很有用的東西,它使得我們可以在一個(gè)用用域中緩存一個(gè)變量,以便后面在另一個(gè)上下文中進(jìn)行使用。由于這個(gè)lambda表達(dá)式使用了它的定義域中的變量,這種情況也被稱作閉包。關(guān)于詞法作用域的訪問限制,可以看下31頁的詞法作用域有什么限制嗎?

詞法作用域有什么限制嗎?

在lambda表達(dá)式中,我們只能訪問它的定義域中的final類型或者實(shí)際上是final類型的本地變量。 
lambda表達(dá)式可能馬上就會(huì)被調(diào)用,也可能延遲進(jìn)行調(diào)用,或者從不同的線程發(fā)起調(diào)用。為了避免競(jìng)爭(zhēng)沖突,我們?cè)L問的定義域中的本地變量,一旦初始化后是不允許進(jìn)行修改的。任何修改操作都會(huì)導(dǎo)致編譯異常。

標(biāo)記成final后解決了這個(gè)問題,不過Java并不強(qiáng)迫我們一定要這么標(biāo)記。事實(shí)上,Java看的是兩點(diǎn)。一個(gè)是訪問的這個(gè)變量必須要在定義它的方法中完成初始化,并且要在定義lambda表達(dá)式之前。第二,這些變量的值不能進(jìn)行修改——也就是說,它們事實(shí)上就是final類型的,盡管沒有這么標(biāo)記。 
無狀態(tài)的lambda表達(dá)式是運(yùn)行時(shí)常量,而那些使用了本地變量的lambda表達(dá)則會(huì)有額外的計(jì)算開銷。

在調(diào)用filter方法的時(shí)候我們就可以用checkIfStartsWith方法返回的lambda表達(dá)式了,就像這樣:

 

復(fù)制代碼代碼如下:

final long countFriendsStartN =
friends.stream() .filter(checkIfStartsWith("N")).count();
final long countFriendsStartB = friends.stream()
.filter(checkIfStartsWith("B")).count(); 

 

在調(diào)用filter方法之前 ,我們先調(diào)用了checkIfStartsWith()方法,把想要的字母?jìng)鲄⑦M(jìn)去。這個(gè)調(diào)用很快就返回了一個(gè)lambda表達(dá)式,然后我們把它傳參給filter方法。

通過創(chuàng)建了一個(gè)高階函數(shù)(這里是checkIfStartsWith)并且使用了詞法作用域,我們成功的去除了代碼中的冗余。我們不用再重復(fù)的判斷name是不是以某個(gè)字母開頭了。

重構(gòu),縮小作用域

在前面的例子中我們用了一個(gè)static方法,不過我們不希望用static方法來緩存變量,這樣把我們的代碼搞亂了。最好能把這個(gè)函數(shù)的作用域縮小到使用它的地方。我們可以用一個(gè)Function接口來實(shí)現(xiàn)這個(gè)。

 

復(fù)制代碼代碼如下:

final Function<String, Predicate<String>> startsWithLetter = (String letter) -> {
Predicate<String> checkStarts = (String name) -> name.startsWith(letter);
return checkStarts; };

 

這個(gè)lambda表達(dá)式取代了原來的static方法,它可以放到函數(shù)里面,在需要用到它之前定義一下就好了。startWithLetter變量引用的是一個(gè)入?yún)⑹荢tring,出參是Predicate的Function。

和使用static方法相比,這個(gè)版本簡(jiǎn)單多了,不過我們還可以對(duì)它繼續(xù)重構(gòu)讓它更簡(jiǎn)潔點(diǎn)。從實(shí)際的角度看,這個(gè)函數(shù)和前面的static方法是一樣的;它們都接收一個(gè)String返回一個(gè)Predicate。為了不顯式的聲明一個(gè)Predicate, 我們用一個(gè)lamdba表達(dá)式整個(gè)給替換掉。

 

復(fù)制代碼代碼如下:

final Function<String, Predicate<String>> startsWithLetter = (String letter) -> (String name) -> name.startsWith(letter);

 

我們把那些亂七八糟的東西給干掉了,但是我們還可以去掉類型聲明,讓它更簡(jiǎn)潔一點(diǎn),Java編譯器會(huì)根據(jù)上下文去做類型推導(dǎo)的。我們來看下改進(jìn)后的版本。

復(fù)制代碼代碼如下:

final Function<String, Predicate<String>> startsWithLetter =
letter -> name -> name.startsWith(letter); 


要適應(yīng)這種簡(jiǎn)潔的語法可得下點(diǎn)工夫。如果它亮瞎了你的眼睛的話,先看看別的地方吧。我們已經(jīng)完成了代碼的重構(gòu),現(xiàn)在可以用它來替換掉原來的checkIfStartsWith()方法了,就像這樣:

復(fù)制代碼代碼如下:

final long countFriendsStartN = friends.stream()
.filter(startsWithLetter.apply("N")).count();
final long countFriendsStartB = friends.stream()
.filter(startsWithLetter.apply("B")).count();

 

在這節(jié)中我們用到了高階函數(shù)。我們看到了如果把函數(shù)傳遞給另一個(gè)函數(shù),如何在函數(shù)中創(chuàng)建函數(shù),以及如何通過函數(shù)來返回一個(gè)函數(shù)。這些例子都展示了lambda表達(dá)式帶來的簡(jiǎn)潔性和可重用性。

本節(jié)中我們充分發(fā)揮了Function和Predicate的作用,不過我們來看下它們兩個(gè)到底有什么區(qū)別。Predicate接受一個(gè)類型為T的參數(shù),返回一個(gè)boolean值來代表它對(duì)應(yīng)的判斷條件的真假。當(dāng)我們需要做條件判斷的時(shí)候,我們可以使用Predicateg來完成。像filter這類對(duì)元素進(jìn)行篩選的方法都接收Predicate作為參數(shù)。而Funciton代表的是一個(gè)函數(shù),它的入?yún)⑹穷愋蜑門的變量,返回的是R類型的一個(gè)結(jié)果。它和只能返回boolean的Predicate相比要更加通用。只要是將輸入轉(zhuǎn)化成一個(gè)輸出的,我們都可以使用Function,因此map使用Function作為參數(shù)也是情理之中的事情了。

可以看到,從集合中選取元素非常簡(jiǎn)單。下面我們將介紹下如何從集合中只挑選出一個(gè)元素。

延伸 · 閱讀

精彩推薦
548
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25
主站蜘蛛池模板: 99精品国产小情侣高潮露脸在线 | 亚洲成人久久精品 | 久久99国产精品免费网站 | 午夜精品成人 | 成年人性视频 | 久久国产精品久久久久久久久久 | 欧美性猛交xxx乱大交3蜜桃 | 欧美成人一区二区三区电影 | 性猛aa久久久 | 国产无区一区二区三麻豆 | 国产成人在线免费视频 | 在线中文字幕网站 | 一区二区三级视频 | av资源在线 | 国产高潮好爽受不了了夜色 | xxxx18韩国护士hd老师 | 91香蕉国产亚洲一区二区三区 | 欧美日韩免费在线观看视频 | 色中色在线播放 | 伊人久久国产精品 | 久久久婷婷 | 中文字幕一区久久 | 蜜桃网在线观看 | 日本不卡二区 | 手机在线看片国产 | 精品不卡 | 九九精品在线观看视频 | 99精品视频在线 | 国产精品自拍啪啪 | 成人男女啪啪免费观看网站四虎 | 欧美又黄又嫩大片a级 | 中文字幕综合在线观看 | 日本不卡二区 | 九九视频久久 | 亚洲午夜影院在线观看 | 欧美一区二区三区久久精品视 | 成人午夜a | 亚洲小视频在线观看,com | 国产人成免费爽爽爽视频 | 黄网站免费观看视频 | 亚洲精品7777xxxx青睐 |