開(kāi)始
上大學(xué)的時(shí)候就知道java8肖揣,lambda表達(dá)式帘腹。記得有段時(shí)間在寫(xiě)匿名內(nèi)部類(lèi)的時(shí)候,編譯器會(huì)提示可以使用lambda表達(dá)式進(jìn)行優(yōu)化许饿。雖然對(duì)Java8有一丟丟了解阳欲,但是工作至今還沒(méi)有真正的使用過(guò)java8的東西。于是現(xiàn)在讀了一下《java8實(shí)戰(zhàn)》學(xué)習(xí)了一下,現(xiàn)在先簡(jiǎn)單總結(jié)一下lambda表達(dá)式球化。
了解Lambda表達(dá)式
可以把Lambda表達(dá)式理解為簡(jiǎn)潔地表示可傳遞的匿名函數(shù)的一種方式:它沒(méi)有名稱(chēng)秽晚,但他有參數(shù)列表、函數(shù)主題筒愚、返回類(lèi)型赴蝇。使用Lambda可以讓代碼變得更簡(jiǎn)潔、更靈活巢掺。在以前使用匿名內(nèi)部類(lèi)的地方可以使用Lambda來(lái)進(jìn)行優(yōu)化處理句伶。
Lambda的基本語(yǔ)法是:
(parameters) -> expression
或者是
(parameters) -> { expression; }
以下是五個(gè)Lambda表達(dá)式的示例:
示例1:有一個(gè)String類(lèi)型的參數(shù),并返回一個(gè)int陆淀。表達(dá)式?jīng)]有retuern語(yǔ)句考余,因?yàn)橐呀?jīng)隱含了return。
(String s) -> s.length()
示例2:有個(gè)Apple類(lèi)型的參數(shù)轧苫,并返回一個(gè)boolean楚堤。
(Apple a) -> a.getWeight() > 150;
示例3:有兩個(gè)int類(lèi)型的參數(shù),沒(méi)有返回值含懊。大括號(hào)包圍可以寫(xiě)多行身冬。
(int x, int y) -> {
System.out.println("Result:");
System.out.println(x + y);
}
示例4:沒(méi)有參數(shù),有一個(gè)int的返回值岔乔。
() -> 42
示例5:有兩個(gè)Apple的參數(shù)酥筝,有一個(gè)boolean返回值。
(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())
使用場(chǎng)景以及如何使用Lambda表達(dá)式
首先給一個(gè)完整的Lambda的使用示例雏门。
篩選函數(shù):
public static <T> List<T> filter(List<T> list, Predicate<T> p) {
List<T> result = new ArrayList<>();
for (T e : list) {
if (p.test(e)) {
result.add(e);
}
}
return result;
}
調(diào)用代碼:
List<Apple> redApples = filter(inventory, (Apple apple) -> "red".equals(apple.getColor()));
調(diào)用代碼的第二個(gè)參數(shù)傳入了一段lambda表達(dá)式嘿歌。需要注意的是第二個(gè)參數(shù)的類(lèi)型是Predicate<t style="margin: 0px; padding: 0px;">,這是一個(gè)函數(shù)式接口〗伺洌現(xiàn)在就回答小節(jié)題目中的一個(gè)問(wèn)題了,可以在函數(shù)式接口上使用Lambda表達(dá)式阅束。</t>
函數(shù)式接口
函數(shù)式接口是只定義一個(gè)抽象方法的接口呼胚。 Lambda表達(dá)式允許你直接以?xún)?nèi)聯(lián)的形式為函數(shù)式接口的抽象方法提供實(shí)現(xiàn),并把整個(gè)表達(dá)式作為函數(shù)式接口的實(shí)例息裸。(簡(jiǎn)單說(shuō)來(lái)蝇更,Lambda表達(dá)式是函數(shù)式接口的實(shí)例)。用匿名內(nèi)部類(lèi)也能完成同樣的事情呼盆,但是會(huì)笨重點(diǎn)年扩,這也是Lambda可以做優(yōu)化的一個(gè)點(diǎn)。
像我們平常用Runnable復(fù)合函數(shù)式接口的定義访圃,所以也是一個(gè)函數(shù)式接口厨幻。為了使用方便Java也提供了一些常用的函數(shù)式接口,大家也可以自己定義。關(guān)于函數(shù)式接口后面還會(huì)詳細(xì)介紹况脆。
函數(shù)描述符
函數(shù)描述符要表達(dá)的是一個(gè)抽象方法接受什么樣的參數(shù)饭宾,有什么樣的返回值的一個(gè)概念。例如Runnable接口函數(shù)描述符是什么也不接受什么也返回格了。因?yàn)樗ㄒ坏某橄蠓椒╮un看铆,什么也不接受什么也不返回。
Lambda表達(dá)式可以接受參數(shù)并有返回值盛末,必然就需要這個(gè)Lambda的函數(shù)的簽名(接受參數(shù)和返回值)要和函數(shù)式接口相匹配弹惦。
例如下面代碼,Runnable的簽名是什么都不接受什么都不返回悄但。
public void process(Runnable r) {
r.run();
}
調(diào)用代碼棠隐,傳入的lambda表達(dá)式的簽名也是什么都不接受,什么都不返回算墨。所以它們可以匹配起來(lái)宵荒。
process(()-> System.out.println("funny新青年"));
小提示:
建議給函數(shù)式接口加@FunctionalInterface的注解净嘀,如果該接口不符合函數(shù)式接口的定義的時(shí)候編譯器將會(huì)報(bào)錯(cuò)报咳。
函數(shù)式接口
首先溫習(xí)下上節(jié)的關(guān)鍵定義。函數(shù)式接口是只定義一個(gè)抽象方法的接口挖藏。 函數(shù)式接口的抽象方法的簽名成為函數(shù)描述符暑刃。
為了適應(yīng)不同的Lambda表達(dá)式,需要有一套能夠描述常見(jiàn)函數(shù)描述符的函數(shù)式接口膜眠。Java Api中已經(jīng)提供了一些常用的岩臣,免的每次使用Lambda表達(dá)式還需要自己定義函數(shù)式接口。
Java8新提供的函數(shù)式接口在java.util.function包下宵膨。具體可以查看java8 api doc(https://docs.oracle.com/javase/8/docs/api/)架谎。
Predicate
java.util.function.Predicate<t style="margin: 0px; padding: 0px;">接口定義了一個(gè)名叫test的抽象方法,他接受泛型T辟躏,返回boolean谷扣。</t>
Consumer
java.util.function.Predicate<t style="margin: 0px; padding: 0px;">接口定義了一個(gè)名叫accept的抽象方法,它接受一個(gè)泛型T的對(duì)象捎琐,沒(méi)有返回会涎。算是消費(fèi)一個(gè)對(duì)象。</t>
Function
java.util.function.Function<t, r="" style="margin: 0px; padding: 0px;">接口定義了一個(gè)名叫apply的抽象方法瑞凑,它接受一個(gè)泛型T的對(duì)象末秃,返回一個(gè)泛型R對(duì)象。是將輸入經(jīng)過(guò)計(jì)算得到一個(gè)輸出接口籽御。</t,>
Supplier
定義了一個(gè)名為get的抽象方法练慕,代表的函數(shù)描述符是() -> T惰匙。
因?yàn)楹瘮?shù)式接口中的泛型只能綁定到引用類(lèi)型,如果想使用基本類(lèi)型可以使用基本類(lèi)型對(duì)應(yīng)的引用類(lèi)型贺待,然后采用自動(dòng)裝箱徽曲,但是這會(huì)導(dǎo)致一些性能損耗。所以Java還給對(duì)應(yīng)的函數(shù)式接口提供了基礎(chǔ)類(lèi)型的特殊處理麸塞。比如給Predicate接口提供了IntPredicate秃臣、LongPredicate、DoublePredicate,分別是傳入int哪工、long奥此、double返回boolean。其他函數(shù)式接口也同樣有這樣的原始類(lèi)型特化處理雁比。
以上提供的函數(shù)式接口僅允許傳入一個(gè)參數(shù)稚虎,為了更靈活一些。java還提供了對(duì)應(yīng)兩個(gè)參數(shù)的函數(shù)式接口有BiPredicate<l, r="" style="margin: 0px; padding: 0px;">偎捎、BiConsumer<t, u="" style="margin: 0px; padding: 0px;">蠢终、BiFunction<t, r="" style="margin: 0px; padding: 0px;">。</t,></t,></l,>
全部的函數(shù)式接口自己去java8 api doc(https://docs.oracle.com/javase/8/docs/api/) 找吧茴她。
方法引用
方法引用可以讓你使用已有的方法實(shí)現(xiàn)Lambda表達(dá)式寻拂,將目標(biāo)引用放在分隔符::之前,方法名放在::后面丈牢。方法引用可以視為僅僅涉及單一方法的Lambda的語(yǔ)法糖祭钉,優(yōu)點(diǎn)是代碼能寫(xiě)的更少,更清晰一點(diǎn)己沛。
那么如何構(gòu)建方法引用呢慌核?方法引用有三類(lèi)。
指向靜態(tài)方法的方法引用申尼。(例如Integer的parseInt方法垮卓,方法引用可以寫(xiě)作Integer::parseInt,對(duì)用的Lambda表達(dá)式為(String s)-> Integer.parseInt(s))师幕。
指向任意類(lèi)型方法的方法引用粟按。(Lambda要調(diào)用參數(shù)中的一個(gè)方法,例如String的length方法们衙,方法引用可以寫(xiě)作String::length钾怔,對(duì)應(yīng)的Lambda表達(dá)式為 (String s) -> s.length())碱呼。
指向現(xiàn)有對(duì)象的實(shí)例方法引用蒙挑。(Lambda要調(diào)用一個(gè)外部的對(duì)象的方法,例如外部有一個(gè)expr對(duì)象愚臀,要調(diào)用他的一個(gè)方法忆蚀,方法應(yīng)用可以寫(xiě)成expr::instanceMethod,對(duì)應(yīng)的Lambda可以寫(xiě)作(args) -> expr.instanceMethod(args)。
構(gòu)造函數(shù)引用
構(gòu)造函數(shù)的引用是方法引用的一個(gè)特殊方法馋袜。
對(duì)于一個(gè)現(xiàn)有的構(gòu)造函數(shù)可以使用類(lèi)名稱(chēng)和new關(guān)鍵字來(lái)創(chuàng)建他的一個(gè)引用:className::new男旗。他的功能與靜態(tài)方法的引用類(lèi)似。
構(gòu)造函數(shù)的引用的返回值肯定是確定的欣鳖。就是className察皇,問(wèn)題在于有幾個(gè)參數(shù)。對(duì)于參數(shù)數(shù)量的不同我們可以選擇不同的簽名泽台。
無(wú)參可以用Supplier什荣,一個(gè)參數(shù)可以用Function,兩個(gè)參數(shù)可以用BiFunction怀酷,三個(gè)四五個(gè)就需要你自己來(lái)實(shí)現(xiàn)一些函數(shù)式接口了稻爬。
復(fù)合Lambda表達(dá)式
java8允許你將簡(jiǎn)單的Lambda表達(dá)式復(fù)合成更復(fù)雜的表達(dá)式。下面講會(huì)比較常用的兩種謂詞復(fù)合和函數(shù)符合蜕依。
謂詞復(fù)合
謂詞接口包含三個(gè)方法:negate桅锄、and和or。你可以重用Predicate來(lái)創(chuàng)建更復(fù)雜的謂詞样眠。
比如你原有一個(gè)謂詞一個(gè)謂詞是從一堆蘋(píng)果中取出紅色的那些∮蚜觯現(xiàn)在想要取出不是紅色的那些,只需要使用negate接口就可以了吹缔。
Predicate<T> notRedApple = redApple.negate();
類(lèi)似的商佑,如果你想得到又紅又大的蘋(píng)果,可以將篩選紅色蘋(píng)果的謂詞和重量更大的蘋(píng)果的謂詞用and連接起來(lái)厢塘。
Predicate<T> notRedApple = redApple.and(a -> a.getWeight > 150);
函數(shù)復(fù)合
你還可以把Function接口所代表的Lambda表達(dá)式組合起來(lái)茶没,有兩個(gè)對(duì)應(yīng)的默認(rèn)方法andThen和compose。
andThen是先執(zhí)行完前面的函數(shù)再執(zhí)行后面的晚碾,compose則相反抓半。例如有兩個(gè)函數(shù),f(x)和g(x)格嘁。f.andThen(g)相當(dāng)于g(f(x))笛求,f.compose(g)相當(dāng)于f(g(x))。
最后
本文內(nèi)容基本是根據(jù)《java8實(shí)現(xiàn)》第三章編寫(xiě)的糕簿,將自己認(rèn)為重要的內(nèi)容拿出來(lái)說(shuō)了一說(shuō)探入。值得一提的是寫(xiě)一篇文章比單純的看書(shū)理解到的內(nèi)容多多了。打完收工懂诗!
歡迎關(guān)注~~~
歡迎關(guān)注【Funny 新青年】微信公眾號(hào)蜂嗽。