亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

 更新時間:2014年09月25日 12:05:42   作者:deepinmind  
這篇文章主要介紹了Java函數(shù)式編程(五):閉包,本文是系列文章的第5篇,其它篇章請參閱相關文章,需要的朋友可以參考下

使用詞法作用域和閉包

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

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

lambda表達式帶來的冗余

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

復制代碼 代碼如下:

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產(chǎn)生了冗余,它們只是那個檢查的字母不同而已。我們來看下如何能避免這種冗余。

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

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

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

復制代碼 代碼如下:

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

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

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

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

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

標記成final后解決了這個問題,不過Java并不強迫我們一定要這么標記。事實上,Java看的是兩點。一個是訪問的這個變量必須要在定義它的方法中完成初始化,并且要在定義lambda表達式之前。第二,這些變量的值不能進行修改——也就是說,它們事實上就是final類型的,盡管沒有這么標記。
無狀態(tài)的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方法。

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

重構,縮小作用域

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

復制代碼 代碼如下:

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

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

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

復制代碼 代碼如下:

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

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

復制代碼 代碼如下:

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

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

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

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

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

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

相關文章

  • Java后臺實現(xiàn)微信支付和微信退款

    Java后臺實現(xiàn)微信支付和微信退款

    這篇文章主要介紹了Java后臺實現(xiàn)微信支付和微信退款,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-03-03
  • Java基于高精度整型實現(xiàn)fibonacci數(shù)列的方法

    Java基于高精度整型實現(xiàn)fibonacci數(shù)列的方法

    這篇文章主要介紹了Java基于高精度整型實現(xiàn)fibonacci數(shù)列的方法,是比較典型的算法,需要的朋友可以參考下
    2014-09-09
  • Java的stream流多個字段排序的實現(xiàn)

    Java的stream流多個字段排序的實現(xiàn)

    本文主要介紹了Java的stream流多個字段排序的實現(xiàn),主要是兩種方法,第一種是固定多個字段排序和第二種動態(tài)字段進行排序,具有一定的參考價值,感興趣的可以了解一下
    2023-10-10
  • 基于java URL和URLConnection(詳解)

    基于java URL和URLConnection(詳解)

    下面小編就為大家分享一篇基于java URL和URLConnection(詳解),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-01-01
  • 解決Maven項目加載spring bean的配置xml文件會提示找不到問題

    解決Maven項目加載spring bean的配置xml文件會提示找不到問題

    這篇文章主要介紹了解決Maven項目加載spring bean的配置xml文件會提示找不到問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • java.net.UnknownHostException異常的一般原因及解決步驟

    java.net.UnknownHostException異常的一般原因及解決步驟

    關于java.net.UnknownHostException大家也許都比較熟悉,這篇文章主要給大家介紹了關于java.net.UnknownHostException異常的一般原因及解決步驟,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2024-02-02
  • mybatis foreach 循環(huán) list(map)實例

    mybatis foreach 循環(huán) list(map)實例

    這篇文章主要介紹了mybatis foreach 循環(huán) list(map)實例,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • 詳解Java的Exception異常機制

    詳解Java的Exception異常機制

    Java的Exception異常機制,為什么會突然聊到異常?其實不是突然,而是我已經(jīng)準備了很久,但苦于沒有好的例子來講解,從表象到底層實現(xiàn),今天就帶大家詳細了解Exception異常,需要的朋友可以參考下
    2021-05-05
  • Java中Volatile關鍵字詳解及代碼示例

    Java中Volatile關鍵字詳解及代碼示例

    這篇文章主要介紹了Java中Volatile關鍵字詳解及代碼示例,分為兩個部分,第一部分介紹了Volatile關鍵字的基本概念等內(nèi)容,第二部分分享了實例代碼,具有一定參考價值,需要的朋友可以了解下。
    2017-11-11
  • Java System類兩個常用方法代碼實例

    Java System類兩個常用方法代碼實例

    這篇文章主要介紹了Java System類兩個常用方法代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-02-02

最新評論