亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Java > 正文

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

2019-11-26 15:24:34
字體:
來源:轉載
供稿:網友

使用詞法作用域和閉包

很多開發人員都存在這種誤解,認為使用lambda表達式會導致代碼冗余,降低代碼質量。恰恰相反,就算代碼變得再復雜,我們也不會為了代碼的簡潔性而在代碼質量上做任何妥協,下面我們就會看到。

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

lambda表達式帶來的冗余

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

復制代碼 代碼如下:

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();

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

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

第一個方案,我們可以把字母抽出來作為函數的參數,同時把這個函數傳遞給filter方法。這是個不錯的方法,不過filter可不是什么函數都接受的。它只接受只有一個參數的函數,那個參數對應的就是集合中的元素,返回一個boolean值,它希望傳進來的是一個Predicate。

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

復制代碼 代碼如下:

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

我們定義了一個靜態函數checkIfStartsWith,它接收一個String參數,并且返回一個Predicate對象,它正好可以傳遞給filter方法,以便后面進行使用。不像前面看到的高階函數那樣是以函數作為參數的,這個方法返回的是一個函數。不過它也是一個高階函數,這個我們在12頁的進化,而非變革中已經提到過了。

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

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

在lambda表達式中,我們只能訪問它的定義域中的final類型或者實際上是final類型的本地變量。
lambda表達式可能馬上就會被調用,也可能延遲進行調用,或者從不同的線程發起調用。為了避免競爭沖突,我們訪問的定義域中的本地變量,一旦初始化后是不允許進行修改的。任何修改操作都會導致編譯異常。

標記成final后解決了這個問題,不過Java并不強迫我們一定要這么標記。事實上,Java看的是兩點。一個是訪問的這個變量必須要在定義它的方法中完成初始化,并且要在定義lambda表達式之前。第二,這些變量的值不能進行修改――也就是說,它們事實上就是final類型的,盡管沒有這么標記。
無狀態的lambda表達式是運行時常量,而那些使用了本地變量的lambda表達則會有額外的計算開銷。

在調用filter方法的時候我們就可以用checkIfStartsWith方法返回的lambda表達式了,就像這樣:

復制代碼 代碼如下:

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

在調用filter方法之前 ,我們先調用了checkIfStartsWith()方法,把想要的字母傳參進去。這個調用很快就返回了一個lambda表達式,然后我們把它傳參給filter方法。

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

重構,縮小作用域

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

復制代碼 代碼如下:

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

這個lambda表達式取代了原來的static方法,它可以放到函數里面,在需要用到它之前定義一下就好了。startWithLetter變量引用的是一個入參是String,出參是Predicate的Function。

和使用static方法相比,這個版本簡單多了,不過我們還可以對它繼續重構讓它更簡潔點。從實際的角度看,這個函數和前面的static方法是一樣的;它們都接收一個String返回一個Predicate。為了不顯式的聲明一個Predicate, 我們用一個lamdba表達式整個給替換掉。

復制代碼 代碼如下:

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

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

復制代碼 代碼如下:

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

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

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

在這節中我們用到了高階函數。我們看到了如果把函數傳遞給另一個函數,如何在函數中創建函數,以及如何通過函數來返回一個函數。這些例子都展示了lambda表達式帶來的簡潔性和可重用性。

本節中我們充分發揮了Function和Predicate的作用,不過我們來看下它們兩個到底有什么區別。Predicate接受一個類型為T的參數,返回一個boolean值來代表它對應的判斷條件的真假。當我們需要做條件判斷的時候,我們可以使用Predicateg來完成。像filter這類對元素進行篩選的方法都接收Predicate作為參數。而Funciton代表的是一個函數,它的入參是類型為T的變量,返回的是R類型的一個結果。它和只能返回boolean的Predicate相比要更加通用。只要是將輸入轉化成一個輸出的,我們都可以使用Function,因此map使用Function作為參數也是情理之中的事情了。

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

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产在线精品一区免费香蕉| 中文字幕亚洲色图| 日韩av大片免费看| 日本免费一区二区三区视频观看| 亚洲精品资源美女情侣酒店| 日韩av一区在线观看| 精品国产鲁一鲁一区二区张丽| 成人黄在线观看| 亚洲自拍在线观看| 在线精品高清中文字幕| 日韩欧美在线第一页| 欧美午夜久久久| 国产噜噜噜噜久久久久久久久| 92国产精品久久久久首页| 精品成人国产在线观看男人呻吟| 欧美国产日产韩国视频| 日韩中文有码在线视频| 国产精品6699| 国产精品一区二区三区在线播放| 亚洲精品小视频在线观看| 欧美一级视频在线观看| 98午夜经典影视| 91夜夜未满十八勿入爽爽影院| 91老司机精品视频| 欧美午夜美女看片| 亚洲女在线观看| 97超级碰碰碰久久久| 亚洲视频综合网| 国产精品久久久久9999| 成人自拍性视频| 最近中文字幕mv在线一区二区三区四区| 综合网日日天干夜夜久久| 日本中文字幕不卡免费| 精品国产鲁一鲁一区二区张丽| 欧美激情第一页xxx| 亚洲欧美视频在线| 成人黄色片网站| 国产精自产拍久久久久久| 国产视频精品久久久| 欧美午夜丰满在线18影院| 久久99久久99精品免观看粉嫩| 亚洲一区二区三区成人在线视频精品| 国产激情999| 国产日韩欧美在线播放| 精品国偷自产在线| 在线精品高清中文字幕| 亚洲深夜福利网站| 国产欧美一区二区三区久久| 中文在线资源观看视频网站免费不卡| 亚洲精品日韩欧美| 国内精品久久久| 在线观看中文字幕亚洲| 91国产中文字幕| 欧美美女操人视频| 国产精品视频精品视频| 日韩欧美在线字幕| 亚洲国产成人久久综合| 精品福利在线看| 欧美亚洲在线视频| 精品福利樱桃av导航| 亚洲一区二区三区乱码aⅴ蜜桃女| 国产午夜精品全部视频播放| 日韩成人在线视频网站| 亚洲男女自偷自拍图片另类| 国产69精品久久久久久| 亚洲色图狂野欧美| 美女啪啪无遮挡免费久久网站| 国产午夜精品全部视频在线播放| 欧美激情在线观看| 日韩欧美国产激情| 狠狠躁夜夜躁人人爽天天天天97| 亚洲欧洲在线播放| 亚洲精品国产综合久久| 岛国av一区二区在线在线观看| 精品久久久久久| 日韩美女av在线| 色噜噜狠狠狠综合曰曰曰| 国产精品青草久久久久福利99| 欧美视频第一页| 成人激情在线观看| 91精品国产色综合久久不卡98| 亚洲欧美日韩精品久久奇米色影视| 国产精品免费看久久久香蕉| 欧美国产精品va在线观看| 91久久久国产精品| 亚洲日本aⅴ片在线观看香蕉| 久久久久久久久久久人体| 国产一区私人高清影院| 欧美激情a在线| 91精品久久久久久久久久久| 成人av在线网址| 亚洲美女性视频| 黄色成人av网| 国产专区欧美专区| 国产综合香蕉五月婷在线| 日韩精品欧美国产精品忘忧草| 日韩av在线网址| 欧美日韩国产在线播放| 亚洲性av在线| 91久久久久久久久久久| 欧美视频一二三| 日韩一区二区三区xxxx| 色婷婷av一区二区三区在线观看| 亚洲成年人在线| 国产亚洲欧美日韩精品| 久久久久久久久久久国产| 欧美床上激情在线观看| 国产成人精品免费久久久久| 97国产精品视频| 亚洲女人天堂视频| 国产精品三级久久久久久电影| 欧美在线国产精品| 欧美午夜激情小视频| 色99之美女主播在线视频| 亚洲精品福利资源站| 国产精品成人av性教育| 国产精品wwwwww| 8090成年在线看片午夜| 日韩激情片免费| 亚洲免费视频网站| 久久久久亚洲精品| 国产色婷婷国产综合在线理论片a| 欧美一区二区三区免费视| 国产亚洲欧美日韩精品| 久久久成人av| 国产精品三级网站| 91av视频在线播放| 日韩在线视频线视频免费网站| 另类色图亚洲色图| 欧美日韩在线观看视频| 精品久久久久久中文字幕一区奶水| 久久久久这里只有精品| 一区二区三区天堂av| 国产日韩精品一区二区| 人九九综合九九宗合| 中文字幕日韩在线播放| 亚洲毛片在线免费观看| 狠狠做深爱婷婷久久综合一区| 久久精品国产欧美激情| 亚洲国产精品va在线看黑人| 国产精品久久二区| 亚洲天堂网在线观看| 亚洲第一区第二区| 91精品国产综合久久香蕉| 夜夜嗨av一区二区三区四区| 国产精品久久久久久av福利| 97超碰国产精品女人人人爽| 国产精品久久久久久中文字| 亚洲精品资源美女情侣酒店| 亚洲va欧美va国产综合剧情| 国内精品小视频在线观看| 最近中文字幕mv在线一区二区三区四区| 国产精品欧美风情| 亚洲男人第一网站| 欧美国产日韩中文字幕在线| 亚洲天堂男人天堂女人天堂| 欧美日韩亚洲系列| 精品久久久久人成| 欧美激情奇米色| 欧美激情免费在线| 亚洲白虎美女被爆操| 中文字幕精品久久久久| 亚洲欧美日韩在线高清直播| 91最新在线免费观看|