在线不卡日本ⅴ一区v二区_精品一区二区中文字幕_天堂v在线视频_亚洲五月天婷婷中文网站

  • <menu id="lky3g"></menu>
  • <style id="lky3g"></style>
    <pre id="lky3g"><tt id="lky3g"></tt></pre>

    現(xiàn)代編程語言都具備的Lambda到底是個(gè)啥?詳聊Lambda與函數(shù)式接口

    1. Lambda

    咱們首先來說說 Lambda 這個(gè)名字,Lambda 并不是一個(gè)什么的縮寫,它是希臘第十一個(gè)字母 λ 的讀音,同時(shí)它也是微積分函數(shù)中的一個(gè)概念,所表達(dá)的意思是一個(gè)函數(shù)入?yún)⒑统鰠?span id="3p4dbnv" class="wpcom_tag_link">定義,在編程語言中其實(shí)是借用了數(shù)學(xué)中的 λ,并且多了一點(diǎn)含義,在編程語言中功能代表它具體功能的叫法是匿名函數(shù)(Anonymous Function),根據(jù)百科的解釋:

    匿名函數(shù)(英語:Anonymous Function)在計(jì)算機(jī)編程中是指一類無需定義標(biāo)識(shí)符(函數(shù)名)的函數(shù)或子程序。

    到這我們應(yīng)該看懂了,在編程語言中引入了 λ 的數(shù)學(xué)中的意思后,還加入了“匿名”這個(gè)概念,為什么要加它呢?顯然是為了讓開發(fā)者寫起來更加方便,不必去想具體的函數(shù)名,尤其是在流式表達(dá)中,匿名能讓你更加高效。

    接著再來說說Lambda 的歷史,雖然它在 JDK8 發(fā)布之后才正式出現(xiàn),但是在編程語言界,它是一個(gè)具有悠久歷史的東西,最早在 1958 年在Lisp 語言中首先采用,而且雖然Java脫胎于C++,但是C++在2011年已經(jīng)發(fā)布了Lambda 了,但是 JDK8 的 LTS 在2014年才發(fā)布,所以 Java 被人叫做老土不是沒有原因的,現(xiàn)代編程語言則是全部一出生就自帶 Lambda 支持,所以Lambda 其實(shí)是越來越火的一個(gè)節(jié)奏~

    那么Lambda 到底好在哪?不用寫函數(shù)名?其實(shí)我覺得要回答這個(gè)問題首先要明白Lambda 在編程語言方面到底是什么?

    上面也說了,Lambda 在編程語言中往往是一個(gè)匿名函數(shù),也就是說Lambda 是一個(gè)抽象概念,而編程語言提供了配套支持,比如在 Java 中其實(shí)為L(zhǎng)ambda 進(jìn)行配套的就是函數(shù)式接口,通過函數(shù)式接口生成匿名類和方法進(jìn)行Lambda 式的處理。

    那么,既然是這一套規(guī)則我們明白了,那么Lambda 所提供的好處在Java中就是函數(shù)式接口所提供的能力了,函數(shù)式接口往往則是提供了一些通用能力,這些函數(shù)式接口在JDK中也有一套完整的實(shí)踐,那就是 Stream。

    Stream 提供了一套完整的流式處理方法幫助我們進(jìn)行流式調(diào)用,熟悉Stream 的讀者應(yīng)該知道使用它能帶來多么大的便捷,更多關(guān)于 Stream 的知識(shí)可以看我的 延遲執(zhí)行與不可變,系統(tǒng)講解JavaStream數(shù)據(jù)處理 ,在這篇文章中有著詳細(xì)的敘述。

    那么總結(jié)起來,Lambda 在Java中所提供的好處就是使用函數(shù)式接口對(duì)一些問題進(jìn)行了抽象,從而得到了一些通用能力,這些通用能力就是使用Lambda 最大的好處,下面將會(huì)具體講解JDK中都定義了哪些通用能力,看到這的小伙伴可以給本文點(diǎn)個(gè)贊,以示鼓勵(lì)。

    2. 函數(shù)式接口

    在 Java 中,所有的函數(shù)式接口都是以 @Functionallnterface 進(jìn)行標(biāo)注的,就像這樣:

    @FunctionalInterfacepublic interface Runnable { public abstract void run();}復(fù)制代碼

    在一個(gè)接口上打上 @Functionallnterface并且定義一個(gè)抽象方法,這樣的類我們就稱之為函數(shù)式接口,當(dāng)然這個(gè)方法并不一定非要用抽象關(guān)鍵字來修飾,比如:

    @FunctionalInterfacepublic interface Consumer { void accept(T t);}復(fù)制代碼

    當(dāng)然,不寫 @Functionallnterface 注解其實(shí)也沒關(guān)系,但是需要保證,這個(gè)接口只定義了一個(gè)抽象方法,接口的默認(rèn)方法不算,那個(gè)可以稱得上是接口的靜態(tài)方法了。

    為什么只能有一個(gè)抽象方法呢?因?yàn)槟愕淖远x邏輯就是這個(gè)方法的匿名函數(shù),最終會(huì)調(diào)用這個(gè)方法,所以只能有一個(gè)。

    然后你就可以使用 Lambda 表達(dá)式來進(jìn)行書寫了,就像這樣:

    Thread thread = new Thread(() -> { });復(fù)制代碼

    看吧,很方便的寫法就定義了一個(gè)Runnable 的匿名子類出來,不過 Runnable 這種使用Lambda 只是為了生成一個(gè)匿名子類的情況確實(shí)無法完全發(fā)揮Lambda 的作用,Lambda 更大的作用還是在解決具體的問題上,而非創(chuàng)造一個(gè)匿名類。

    舉個(gè)例子,假如你想定義一個(gè)對(duì)商品數(shù)據(jù)進(jìn)行商品篩查的函數(shù),那么它可能是這樣的:

    public List filter(List list, String type) { List result = new ArrayList(); for (Goods goods : list) { if (goods.getType().equals(type)) { result.add(goods); } } return result; }復(fù)制代碼

    ok,看起來一切沒問題,但是架不住需求改變啊,很快你又需要定義一個(gè)對(duì)商品金額進(jìn)行篩查的方法,那么它可能是這樣的:

    public List gt(List list, Integer price) { List result = new ArrayList(); for (Goods goods : list) { if (goods.getPrice() > price) { result.add(goods); } } return result; }復(fù)制代碼

    那么你可以發(fā)現(xiàn):大部分代碼基本沒變,只有入?yún)⒑团袛噙壿嫲l(fā)生了一點(diǎn)改變,這個(gè)時(shí)候你可能會(huì)想,能不能把判斷邏輯直接抽象成一個(gè)匿名函數(shù),每次只需要簡(jiǎn)單寫一個(gè)這個(gè)判斷函數(shù)即可,再把方法入?yún)⒎庋b成一個(gè)東西,在任何場(chǎng)景下都可以使用。

    看到這,你可能就有點(diǎn)明白了,因?yàn)樵贘ava8 已經(jīng)提供了Stream流去做這件事,上面這個(gè)場(chǎng)景其實(shí)對(duì)應(yīng)的是Stream 中的filter 方法,而filter 方法的入?yún)⒕褪且粋€(gè)函數(shù)式接口——Predicate。

    還沒明白嗎?那我說的再清楚一點(diǎn),Predicate 抽象了判斷這個(gè)場(chǎng)景,而且這種抽象不局限于業(yè)務(wù),是直接對(duì)某一類場(chǎng)景進(jìn)行抽象,比如篩選商品類別,篩選商品大于某個(gè)金額或者小于某個(gè)金額,它不在糾結(jié)你到底想要怎么篩選,而是直接對(duì)篩選函數(shù)進(jìn)行抽象,得到了Predicate,你想怎么篩選你自己寫,剩下的交給它,它將一勞永逸的解決這類問題,當(dāng)然這里面還有一部分 Stream 的功勞,不過主要思想還是 Predicate 在做,Stream 這里我們暫且不提。

    像這種對(duì)于某個(gè)場(chǎng)景進(jìn)行頂級(jí)抽象的函數(shù)式接口,JDK一共提供了四個(gè):

  • Consumer
  • Supplier
  • Predicate
  • Function
  • 接下來我將一一為大家進(jìn)行講述,除了這四個(gè)之外還有大量的衍生函數(shù)式接口,在JDK8中就有50個(gè)左右,不過都是在這四個(gè)基礎(chǔ)上進(jìn)行修改,不必?fù)?dān)心記不住的問題。

    3. Consumer

    **Consumer **通過名字可以看出它是一個(gè)消費(fèi)函數(shù)式接口,主要針對(duì)的是消費(fèi)這個(gè)場(chǎng)景,它的代碼定義如下:

    @FunctionalInterfacepublic interface Consumer { void accept(T t);}復(fù)制代碼

    通過泛型 T 定義了一個(gè)入?yún)?,但是沒有返回值,它代表你可以針對(duì)這個(gè)入?yún)⒆鲆恍┳远x邏輯,比較典型的例子是 Stream 中的 forEach 方法。

    而我們的主要使用場(chǎng)景也往往是循環(huán)進(jìn)行某項(xiàng)操作,比如有一堆手機(jī)號(hào),循環(huán)進(jìn)行發(fā)短信。

    所以消費(fèi)場(chǎng)景是 Consumer 的主要用武之地,但是有時(shí)候你還面臨一個(gè)問題,一個(gè)入?yún)⑺坪跆倭?,有時(shí)候你需要對(duì)兩個(gè)對(duì)象進(jìn)行操作,又懶得將它們合并成一個(gè)對(duì)象,這種情況 JDK 提供了 BiConsumer:

    @FunctionalInterfacepublic interface BiConsumer { void accept(T t, U u);}復(fù)制代碼

    這種你可以直接傳進(jìn)去兩個(gè)參數(shù)了,什么?你想要三個(gè)參數(shù)的?那沒有,三個(gè)或者三個(gè)以上我感覺就有必要合并成一個(gè)對(duì)象進(jìn)行消費(fèi)了。

    除了這兩個(gè)之外,還有DoubleConsumer、IntConsumer和LongConsumer這種限定了入?yún)㈩愋偷?Consumer,這里不再多述。

    4. Supplier

    Supplier通過名字比較難看出來它是一個(gè)場(chǎng)景的函數(shù)式接口,它主要針對(duì)的是get這個(gè)場(chǎng)景或者說獲取這個(gè)場(chǎng)景,它的代碼定義如下:

    @FunctionalInterfacepublic interface Supplier { T get();}復(fù)制代碼

    通過泛型 T 定義了一個(gè)返回值類型,但是沒有入?yún)?,它代表你可以針?duì)調(diào)用方獲取某個(gè)值,比較典型的例子是 Stream 中的 collect 方法,通過自定義傳入我們想要取得的某種對(duì)象進(jìn)行對(duì)象收集。

    而我們的主要使用場(chǎng)景也往往是收集和聚合這個(gè)場(chǎng)景了,這個(gè)場(chǎng)景我們也是對(duì)獲得這個(gè)場(chǎng)景進(jìn)行收集。

    和Consumer一樣,Supplier還具有以下衍生接口:

  • BooleanSupplier
  • DoubleSupplier
  • IntSupplier
  • LongSupplier
  • 都是提前對(duì)獲取的定義好了數(shù)據(jù)類型,思想一致,這里不再多述。

    5. Predicate

    Predicate前文我們已經(jīng)介紹過,它主要針對(duì)的是判斷這個(gè)場(chǎng)景,它的代碼定義如下:

    @FunctionalInterfacepublic interface Predicate { boolean test(T t);}復(fù)制代碼

    通過泛型 T 定義了一個(gè)入?yún)ⅲ祷亓艘粋€(gè)布爾值,它代表你可以傳入一段判斷邏輯的函數(shù),比較典型的例子是 Stream 中的 filter方法。

    我們對(duì)于它的使用場(chǎng)景實(shí)在是太多了,基本上做任何業(yè)務(wù)都有在內(nèi)存中進(jìn)行篩選 or 判斷的場(chǎng)景。

    所以判斷和篩選場(chǎng)景是 Predicate的主要用武之地,但是有時(shí)候你還面臨和上面一樣的問題,一個(gè)入?yún)⑺坪跆倭耍袝r(shí)候你需要對(duì)兩個(gè)對(duì)象進(jìn)行操作,又懶得將它們合并成一個(gè)對(duì)象,這種情況 JDK 提供了 BiPredicate:

    @FunctionalInterfacepublic interface BiPredicate { boolean test(T t, U u);}復(fù)制代碼

    這種你可以直接傳進(jìn)去兩個(gè)參數(shù)進(jìn)行函數(shù)的自定義邏輯。

    除了這兩個(gè)之外,還有DoublePredicate、IntPredicate和LongPredicate這種限定了入?yún)㈩愋偷腜redicate,這里不再多述。

    6. Function

    Function 接口的名字不太能輕易看出來它的場(chǎng)景,它主要針對(duì)的則是 轉(zhuǎn)換這個(gè)場(chǎng)景,其實(shí)說轉(zhuǎn)換可能也不太正確,它是一個(gè)覆蓋范圍比較廣的場(chǎng)景,你也可以理解為擴(kuò)展版的Consumer,接口定義如下:

    @FunctionalInterfacepublic interface Function { R apply(T t);}復(fù)制代碼

    通過一個(gè)入?yún)?T 進(jìn)行自定義邏輯處理,最終得到一個(gè)出參 R,比較典型的例子是 Stream 中的 map 系列方法和 reduce 系列方法。

    為什么我說也可以理解為一個(gè)擴(kuò)展版的Consumer呢?我們還舉例手機(jī)號(hào)發(fā)短信的場(chǎng)景好了,你通過循環(huán)發(fā)完短信之后可能想拿到發(fā)完短信之后的結(jié)果對(duì)象,來進(jìn)行后續(xù)處理。

    這個(gè)時(shí)候單純的Consumer就不行了,因?yàn)樗鼪]有返回值,你就可以通過 Function 這種函數(shù)式對(duì)象進(jìn)行處理了。

    和 Consumer 一樣,F(xiàn)unction 也有一個(gè)衍生接口可以通過兩個(gè)入?yún)⒎祷匾粋€(gè)對(duì)象——BiFunction。

    還有一些定義好了入?yún)⒑统鰠⒌?Function

  • 延遲執(zhí)行與不可變,系統(tǒng)講解JavaStream數(shù)據(jù)處理
  • 歸約、分組與分區(qū),深入講解JavaStream終結(jié)操作
  • 鄭重聲明:本文內(nèi)容及圖片均整理自互聯(lián)網(wǎng),不代表本站立場(chǎng),版權(quán)歸原作者所有,如有侵權(quán)請(qǐng)聯(lián)系管理員(admin#wlmqw.com)刪除。
    上一篇 2022年7月24日 17:05
    下一篇 2022年7月24日 17:05

    相關(guān)推薦

    聯(lián)系我們

    聯(lián)系郵箱:admin#wlmqw.com
    工作時(shí)間:周一至周五,10:30-18:30,節(jié)假日休息