如何理解 Java8 的函數(shù)式編程获洲,面試必進

Java8 出現(xiàn)的時間已經(jīng)不算短了,免費維護期馬上也要到期了可训,官方已經(jīng)開始推薦使用 Java11昌妹。

Java8 是革命性的一個版本,一直以來握截,Java 最受詬病的地方就是代碼寫起來很啰嗦飞崖,僅僅寫一個 HelloWorld 都需要些很多的樣板代碼。

Java8 推出之后谨胞,啰嗦的代碼有了很大的改觀固歪,Java 也可以寫出簡單優(yōu)美的代碼。最明顯的改觀就是 Java 開始支持函數(shù)式編程胯努。

函數(shù)式編程的定義很晦澀牢裳,但是我們可以將函數(shù)式編程理解為函數(shù)本身可以作為參數(shù)進行傳遞,就是說叶沛,參數(shù)不僅僅可以是數(shù)據(jù)蒲讯,也可以是行為(函數(shù)或者方法的實現(xiàn)其實就是邏輯行為)。

可能是 Java8 步子跨的太大灰署,以至于現(xiàn)在還有很多人沒有趕上來判帮,依然用 Java8 在寫 Java5 風格的代碼局嘁。

這篇文章的目的就是徹底說清楚 Java8 的變化,以及快速全面的使用 Java8 的特性晦墙,讓 Java 代碼優(yōu)雅起來悦昵。

Java面試突擊、阿里巴巴Java面試筆記晌畅、面試答案關注我私信回復【444】獲得免費獲取方式但指!

函數(shù)式接口

在開始說 Java8 的函數(shù)式編程之前,我們需要說明一下抗楔,在 Java8 中新增加的一個概念棋凳,叫函數(shù)式接口

這個函數(shù)式接口是 Java8 實現(xiàn)函數(shù)式編程的基礎连躏,正是這類接口的存在贫橙,才能把函數(shù)(方法)當做參數(shù)進行傳遞,至少表面上看起來是這樣的反粥,但是實際上傳遞的還是對象,這個問題我們下面再討論疲迂,先回到函數(shù)式接口才顿。

下面就是一個函數(shù)式接口:

publicinterfaceAction{publicvoidaction();}

這個函數(shù)式看起來和普通的接口沒有什么區(qū)別,唯一的區(qū)別是函數(shù)式接口只能有一個抽象方法尤蒿。

如果你想讓別人立馬理解這個接口是函數(shù)式接口郑气,可以加上?@FunctionalInterface?注解,這個注解不會提供任何額外的功能腰池,僅僅用來表示這個接口是一個函數(shù)式接口尾组。

@FunctionalInterfacepublicinterfaceAction{publicvoidaction();}

只能有一個抽象方法是為了更方便的把函數(shù)作為參數(shù)來傳遞,這個后面再細說示弓。

我們可以根據(jù)自己的需要來定義函數(shù)式接口讳侨,JDK 為了使用方便,內(nèi)置了很多函數(shù)式接口奏属,日常使用完全夠了跨跨。

常用的函數(shù)接口有:

1.Function

2.Predicate

3.Consumer

函數(shù)式接口其實就這么簡單,看到這里你可能還是對函數(shù)式接口不是很理解囱皿,沒關系勇婴,現(xiàn)在你僅僅只需要記住函數(shù)式接口就是模板

Lambda 表達式

說起 Java8 的函數(shù)式編程嘱腥,很多人都知道 lambda 表達式耕渴,這也是 Java8 中最容易被人記住的地方。

先來通過一個直觀的例子來了解一下 lambda齿兔,在操作 ArrayList 等數(shù)據(jù)結(jié)構(gòu)時橱脸,我們有可能要對其中的數(shù)據(jù)進行排序础米,比如:

Comparator comparator =newComparator() {publicintcompare(Integer i1, Integer i2){returni1.comparaTo(i2);? ? }}

在上面的代碼中,真正有用的代碼也有比較大小的那行慰技,其他的都是樣板代碼椭盏。在這樣的情況下,lambda 就很有用吻商。

Comparator comparator =(Integer i1, Integer i2)->{returni1.compareTo(i2);}

這樣看起來是不是很簡單了掏颊,但是還是繼續(xù)優(yōu)化,可以把返回參數(shù)的部分也省略:

Comparator comparator =(Integer i1, Integer i2)->i1.compareTo(i2)

既然兩個參數(shù)都是 Integer 那是不是也可以省略艾帐,最后就得到了下面這樣的形式:

Comparator comparator =(i1, i2)->i1.compareTo(i2)

這就是 lambda 的力量乌叶,可以把上面那么多的代碼濃縮成一行。

lambda 其實就是一段代碼柒爸,但也不僅僅是一段代碼准浴,再簡單的 lambda 也會有三部分,參數(shù)列表捎稚,箭頭和 lambda 主體乐横,上面的?(i1, i2)?就是參數(shù)列表,?i1.compareTo(i2)?就是 lambda 主體今野,箭頭把這兩部分隔開了葡公。

lambda 是匿名的,這點和 Java 中的匿名實現(xiàn)類有點像(本質(zhì)上一樣)条霜,而且它是一種函數(shù)催什,不屬于任何類(屬于類的函數(shù)稱之為方法),并且可以作為參數(shù)進行傳遞宰睡,而且還很簡潔蒲凶。

看到這里,可能就有人猜到函數(shù)式接口和 lambda 之間可能有某些關系了拆内,沒錯旋圆,我們已經(jīng)快說到最重要的部分。

在這之前再來理解一個概念麸恍,函數(shù)簽名臂聋,函數(shù)簽名為可以表示一類函數(shù),如果兩個函數(shù)的以下部分相同或南,就可以說這兩個函數(shù)的簽名一致:

函數(shù)參數(shù)及其類型

返回值及其類型

可能會拋出的異常

還有訪問控制符(public 等等)

最關鍵的地方來了孩等,只要 lambda 和函數(shù)式接口方法的簽名一致,lambda 表達式就可以作為參數(shù)傳入到以該函數(shù)式接口為參數(shù)類型的方法中采够。

來看個詳細的例子肄方,?Comparator?接口的定義如下:

@FunctionalInterfacepublicinterfaceComparator{intcompare(T o1, T o2);}

雖然 Comparator 中方法不止一個,但是抽象方法只有?compare?一個蹬癌,上面的 lambda 完全可以作為 compare 方法的實現(xiàn)权她,實際上虹茶,lambda 表達式確實是作為函數(shù)式接口抽象方法的實現(xiàn),而且隅要,lambda 表達式為作為整個函數(shù)接口的實例蝴罪。

到這里,真相大白步清,Java 8 的雖然支持了函數(shù)式編程要门,這不代表函數(shù)就是 Java 中的一等公民了,每一個函數(shù)其實還是被包裹成一個對象廓啊,對象依然是 Java 中的一等公民欢搜。

所以簡單來說,只要 lambda 的表達式的參數(shù)和返回類型可以與函數(shù)式接口中的抽象方法對的上谴轮,lambda 就可以作為該函數(shù)式接口的實現(xiàn)進行傳遞炒瘟。

比如上面列舉的幾種函數(shù)式接口,其實就是對一些通用函數(shù)的抽象第步,比如 Function 函數(shù)式接口如下:

@FunctionalInterfacepublicinterfaceFunction{Rapply(T t);}

這種就代表接受一個參數(shù)疮装,返回另一個值的函數(shù),只要滿足這個要求的 lambda 表達式都可以作為 它的實現(xiàn)粘都。

再比如 Predicate 接口斩个,代表接受一個參數(shù)返回一個布爾值的函數(shù):

@FunctionalInterfacepublicinterfacePredicate{booleantest(T t);}

Comsumer 接口表示接受一個參數(shù),什么都不返回的函數(shù):

@FunctionalInterfacepublicinterfaceConsumer{voidaccept(T t);}

如果這樣理解起來還是有點困難驯杜,那就把這些函數(shù)式接口理解為 lambda 表達式的類型。

類型檢查和類型推斷

在上面我們說到了只要函數(shù)式接口抽象方法的函數(shù)簽名與 lambda 一致做个,那么就可以把 lambda 表達式作為該函數(shù)式接口的實現(xiàn)鸽心。

上面的例子中, lambda 的參數(shù)類型也是可以省略的居暖,那么 Java 是如何判斷 lambda 是否與函數(shù)式接口匹配呢顽频?

如果 lambda 表達式中,參數(shù)和返回值的類型都省略之后太闺,需要從使用 lambda 的上下文推斷出來糯景。

方法引用

本來到這里應該就很完美了,lambda 夠簡潔省骂,用它寫代碼蟀淮,可以省略很多無用的樣本代碼,但是 lambda 也不完美钞澳,因為 lambda 表達式的代碼很難復用怠惶,而且很多的 lambda 表達式僅僅就是調(diào)用了其他的方法。

這個時候轧粟,方法引用就可以派上用場了策治,比如上面的例子脓魏,其實僅僅就是調(diào)用了?Integer.compareTo()?方法:

Comparator comparator =(i1, i2)->i1.compareTo(i2)

還可以簡化成下面的樣子:

Comparatorcomparator = Integer::compareTo

使用方法引用的時候,要使用?::?通惫,而且任何方法都可以這樣被引用茂翔,無論是靜態(tài)方法還是實例方法。

方法引用可以被認為是 lambda 的語法糖履腋,使用方法引用可以讓代碼更加簡潔珊燎,更直觀,看到方法引用的名稱就能大概知道代碼的邏輯府树,并且還可以對一些代碼進行復用俐末。

寫出 Java8 風格的代碼

在 Java8 之后,很多代碼的寫法應該摒棄奄侠,下面列舉一些常見的例子卓箫。

遍歷 List

Java8 以前:

for(Integeri : list) {System.out.println(i);}

Java8 及以后:

list.forEach(System.out::println);

forEach 接收 Consumer 類型的函數(shù),而 System.out.println() 剛好就符合要求垄潮。

遍歷 Map

Java8 以前:

for(Map.Entryentry : map.entrySet()) {System.out.println("Key: "+ entry.getKey() +", Value:"+ entry.getValue()); }

Java8 及以后:

map.forEach((k, v)->{System.out.println("Key: "+ k +", Value: "+ v)});

這里的 forEach 不是接收 Consumer 類型的函數(shù)烹卒,而是接收 BiConsumer 類的函數(shù),可以用來處理 map 這種包含 key-value 類型的數(shù)據(jù)弯洗,在大多數(shù)場景下旅急,內(nèi)置的函數(shù)式接口以及足夠我們使用,只有在一些特殊的場景下才需要自己定制牡整。

這里也沒有現(xiàn)成的方法引用藐吮,所以就可以使用 lambda 表達式來實現(xiàn)。

執(zhí)行異步任務

假如要使用線程池來執(zhí)行任務逃贝。

Java8 以前:

ThreadPoolExecutor executor =newThreadPoolExecutor(10,10,0, TimeUnit.SECONDS,newArrayBlockingQueue(2000));executor.submit(newRunnable() {publicvoidrun(){? ? ? ? System.out.println("Thread pool execute");? ? }});

Java8 以后:

ThreadPoolExecutor executor =newThreadPoolExecutor(10,10,0, TimeUnit.SECONDS,newArrayBlockingQueue(2000));executor.submit(()->{? ? System.out.println("Thread pool execute");});

也可以使用 lambda 來解決這個問題谣辞,Runnable 也是一個函數(shù)式接口。

Java面試突擊沐扳、阿里巴巴Java面試筆記泥从、面試答案關注我私信回復【444】獲得免費獲取方式!












作者【Rayjun】

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末沪摄,一起剝皮案震驚了整個濱河市躯嫉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌杨拐,老刑警劉巖祈餐,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異哄陶,居然都是意外死亡昼弟,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門奕筐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來舱痘,“玉大人变骡,你說我怎么就攤上這事“攀牛” “怎么了塌碌?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長旬盯。 經(jīng)常有香客問我台妆,道長,這世上最難降的妖魔是什么胖翰? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任接剩,我火速辦了婚禮,結(jié)果婚禮上萨咳,老公的妹妹穿的比我還像新娘懊缺。我一直安慰自己,他們只是感情好培他,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布鹃两。 她就那樣靜靜地躺著,像睡著了一般舀凛。 火紅的嫁衣襯著肌膚如雪俊扳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天猛遍,我揣著相機與錄音馋记,去河邊找鬼。 笑死懊烤,一個胖子當著我的面吹牛梯醒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播奸晴,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼日麸!你這毒婦竟也來了寄啼?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤代箭,失蹤者是張志新(化名)和其女友劉穎墩划,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嗡综,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡乙帮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了极景。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片察净。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡驾茴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出氢卡,到底是詐尸還是另有隱情锈至,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布译秦,位于F島的核電站峡捡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏筑悴。R本人自食惡果不足惜们拙,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望阁吝。 院中可真熱鬧砚婆,春花似錦、人聲如沸求摇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽与境。三九已至验夯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間摔刁,已是汗流浹背挥转。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留共屈,地道東北人绑谣。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓负甸,卻偏偏與公主長得像鳄厌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子屿附,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容