日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

lambda捕获this_非捕获Lambda的实例

發(fā)布時(shí)間:2023/12/3 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 lambda捕获this_非捕获Lambda的实例 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

lambda捕獲this

大約一個(gè)月前,我在Java 8的lambda表達(dá)式框架下總結(jié)了Brian Goetz的觀點(diǎn) 。 目前,我正在研究有關(guān)默認(rèn)方法的文章,令我驚訝的是,我又回到了Java處理lambda表達(dá)式的方式。 這兩個(gè)功能的交集可能會(huì)產(chǎn)生微妙但令人驚訝的效果,我想討論一下。

總覽

為了使這一點(diǎn)更有趣,我將以一個(gè)示例開頭,該示例將以我的個(gè)人WTF達(dá)到頂峰? 時(shí)刻。 完整的示例可以在專用的GitHub項(xiàng)目中找到 。

然后,我們將看到有關(guān)此意外行為的解釋,并最終得出一些結(jié)論以防止錯(cuò)誤。

這里有個(gè)例子……它不是那么簡(jiǎn)單或抽象,因?yàn)槲蚁M@示這種情況的相關(guān)性。 但是從某種意義上說(shuō),這仍然是一個(gè)示例,它僅暗示實(shí)際上可能有用的代碼。

功能界面

假設(shè)對(duì)于在構(gòu)建過(guò)程中已經(jīng)存在結(jié)果的場(chǎng)景,我們需要對(duì)Future接口進(jìn)行特殊化。

我們決定通過(guò)創(chuàng)建一個(gè)接口ImmediateFuture來(lái)實(shí)現(xiàn)此目的,該接口get()使用默認(rèn)方法實(shí)現(xiàn)除get()之外的所有功能。 這導(dǎo)致功能界面 。

您可以在此處查看源代碼。

一個(gè)工廠

接下來(lái),我們實(shí)現(xiàn)FutureFactory 。 它可能會(huì)創(chuàng)建各種期貨,但肯定會(huì)創(chuàng)建我們的新子類型。 它是這樣的:

未來(lái)工廠

/*** Creates a new future with the default result.*/ public static Future<Integer> createWithDefaultResult() {ImmediateFuture<Integer> immediateFuture = () -> 0;return immediateFuture; }/*** Creates a new future with the specified result.*/ public static Future<Integer> createWithResult(Integer result) {ImmediateFuture<Integer> immediateFuture = () -> result;return immediateFuture; }

創(chuàng)造未來(lái)

最后,我們使用工廠創(chuàng)建一些期貨并將其收集在一組中:

創(chuàng)建實(shí)例

public static void main(String[] args) {Set<Future<?>> futures = new HashSet<>();futures.add(FutureFactory.createWithDefaultResult());futures.add(FutureFactory.createWithDefaultResult());futures.add(FutureFactory.createWithResult(42));futures.add(FutureFactory.createWithResult(63));System.out.println(futures.size()); }

WTF ?!

運(yùn)行程序。 控制臺(tái)會(huì)說(shuō)...

4? 不。 3。

WTF ?!

Lambda表達(dá)式的評(píng)估

那么這是怎么回事? 那么,與有關(guān)lambda表達(dá)式的評(píng)估一些背景知識(shí),這其實(shí)并不奇怪。 如果您不太熟悉Java的實(shí)現(xiàn)方式,那么現(xiàn)在是趕上Java的好時(shí)機(jī)。 做到這一點(diǎn)的一種方法是觀看Brian Goetz的演講“ Java中的Lambdas:深入了解”或閱讀我的摘要 。

Lambda表達(dá)式的實(shí)例

理解這種行為的關(guān)鍵在于,事實(shí)是JRE不保證如何將lambda表達(dá)式轉(zhuǎn)換為相應(yīng)接口的實(shí)例。 讓我們看一下Java語(yǔ)言規(guī)范對(duì)此事的看法:

15.27.4。 Lambda表達(dá)式的運(yùn)行時(shí)評(píng)估

[…]

分配并初始化具有以下屬性的類的新實(shí)例,或者引用具有以下屬性的類的現(xiàn)有實(shí)例。

[…類的屬性–這里不足為奇…]

這些規(guī)則旨在通過(guò)以下方式為Java編程語(yǔ)言的實(shí)現(xiàn)提供靈活性:

  • 不必在每次評(píng)估中分配一個(gè)新對(duì)象。
  • 由不同的lambda表達(dá)式產(chǎn)生的對(duì)象不必屬于不同的類(例如,如果主體相同)。
  • 評(píng)估產(chǎn)生的每個(gè)對(duì)象不必屬于同一類(例如,可以內(nèi)聯(lián)捕獲的局部變量)。
  • 如果“現(xiàn)有實(shí)例”可用,則不需要在先前的lambda評(píng)估中創(chuàng)建它(例如,可能在封閉類的初始化期間分配了它)。
[…]

JLS,Java SE 8版,§15.27.4

在其他優(yōu)化中,這顯然使JRE可以返回相同的實(shí)例,以重復(fù)評(píng)估lambda表達(dá)式。

非捕獲Lambda表達(dá)式的實(shí)例

請(qǐng)注意,在上面的示例中,表達(dá)式不捕獲任何變量。 因此,它永遠(yuǎn)不會(huì)因評(píng)估而改變。 而且由于lambda并非設(shè)計(jì)為具有狀態(tài),因此不同的評(píng)估在其生命周期中也無(wú)法“分散”。 因此,一般而言,沒(méi)有充分的理由來(lái)創(chuàng)建多個(gè)不捕獲的lambda實(shí)例,因?yàn)樗鼈冊(cè)谡麄€(gè)生命周期中都完全相同。 這樣可以使優(yōu)化始終返回相同的實(shí)例。

(將其與捕獲某些變量的lambda表達(dá)式進(jìn)行對(duì)比。對(duì)此表達(dá)式的直接評(píng)估是創(chuàng)建一個(gè)將捕獲的變量作為字段的類。每個(gè)評(píng)估都必須創(chuàng)建一個(gè)新實(shí)例,將捕獲的變量存儲(chǔ)在其字段中這些情況顯然并不普遍。)

這就是上面代碼中發(fā)生的事情。 () -> 0是一個(gè)不捕獲的lambda表達(dá)式,因此每個(gè)評(píng)估都返回相同的實(shí)例。 因此,對(duì)createWithDefaultResult()每次調(diào)用都是如此。

但是,請(qǐng)記住,這僅適用于當(dāng)前安裝在我的計(jì)算機(jī)上的JRE版本(用于Win 64的Oracle 1.8.0_25-b18)。 您的可以有所不同,下一個(gè)gal也可以如此等等。

得到教訓(xùn)

因此,我們了解了為什么會(huì)這樣。 盡管這很有意義,但我仍然會(huì)說(shuō)這種行為并不明顯,因此并不是每個(gè)開發(fā)人員都期望的。 這是漏洞的滋生地,因此讓我們嘗試分析情況并從中學(xué)習(xí)一些東西。

使用默認(rèn)方法進(jìn)行子類型化

可以說(shuō),意外行為的根本原因是如何完善Future的決定。 為此,我們擴(kuò)展了另一個(gè)接口,并使用默認(rèn)方法實(shí)現(xiàn)了部分功能。 僅剩一個(gè)未實(shí)現(xiàn)的方法, ImmediateFuture成為了一個(gè)啟用lambda表達(dá)式的功能接口。

另外, ImmediateFuture可以是抽象類。 這樣可以防止工廠意外返回相同的實(shí)例,因?yàn)樗荒苁褂胠ambda表達(dá)式。

關(guān)于抽象類和默認(rèn)方法的討論不容易解決,因此在這里我不嘗試這樣做。 但是,我很快將發(fā)布有關(guān)默認(rèn)方法的文章,并且我打算再講一遍。 可以說(shuō),在做出決定時(shí)應(yīng)考慮此處提出的案例。

工廠中的Lambda

由于lambda的引用相等性不可預(yù)測(cè),因此工廠方法應(yīng)仔細(xì)考慮使用它們來(lái)創(chuàng)建實(shí)例。 除非方法的協(xié)定明確允許不同的調(diào)用返回相同的實(shí)例,否則應(yīng)完全避免使用它們。

我建議在此禁令中包括捕獲lambda。 (對(duì)我而言)一點(diǎn)也不清楚,在什么情況下可以在將來(lái)的JRE版本中重用同一實(shí)例。 一種可能的情況是,JIT發(fā)現(xiàn)緊密的循環(huán)創(chuàng)建了總是(或至少經(jīng)常)返回同一實(shí)例的供應(yīng)商。 通過(guò)用于不捕獲lambda的邏輯,重用同一供應(yīng)商實(shí)例將是有效的優(yōu)化。

匿名類與Lambda表達(dá)式

注意匿名類和lambda表達(dá)式的不同語(yǔ)義。 前者保證創(chuàng)建新實(shí)例,而后者則不能。 為了繼續(xù)該示例,以下createWithDefaultResult()將導(dǎo)致futures –大小為4的集合:

匿名類的替代實(shí)現(xiàn)

public static Future<Integer> createWithDefaultResult() {ImmediateFuture<Integer> immediateFuture = new ImmediateFuture<Integer>() {@Overridepublic Integer get() throws InterruptedException, ExecutionException {return 0;}};return immediateFuture; }

這尤其令人不安,因?yàn)樵S多IDE允許從匿名接口實(shí)現(xiàn)到lambda表達(dá)式的自動(dòng)轉(zhuǎn)換,反之亦然。 由于兩者之間存在細(xì)微的差異,這種看似純粹的句法轉(zhuǎn)換會(huì)帶來(lái)細(xì)微的行為變化。 (我最初并不了解。)

萬(wàn)一您遇到了這種情況,并選擇使用匿名類,請(qǐng)確保以明顯方式記錄您的決定! 不幸的是,似乎沒(méi)有辦法阻止Eclipse對(duì)其進(jìn)行任何轉(zhuǎn)換(例如,如果將轉(zhuǎn)換作為保存操作啟用),這也會(huì)刪除匿名類中的所有注釋。

最終的替代方案似乎是一個(gè)(靜態(tài))嵌套類。 我知道沒(méi)有哪個(gè)IDE敢將其轉(zhuǎn)換為lambda表達(dá)式,因此這是最安全的方法。 盡管如此,仍需要對(duì)其進(jìn)行文檔記錄,以防止下一個(gè)Java-8迷(確實(shí)像您一樣)出現(xiàn)并加緊您的仔細(xì)考慮。

功能接口標(biāo)識(shí)

當(dāng)您依賴功能接口的標(biāo)識(shí)時(shí)要小心。 始終考慮是否有可能,無(wú)論您在何處獲得這些實(shí)例,都可能反復(fù)將您交給同一個(gè)實(shí)例。

但這當(dāng)然是模糊的,幾乎沒(méi)有什么具體的結(jié)果。 首先,所有其他接口都可以簡(jiǎn)化為功能接口。 這實(shí)際上就是我選擇Future的原因-我想舉一個(gè)不會(huì)立即尖叫瘋狂的LAMBDA狗屎的例子! 其次,這會(huì)使您很快變得偏執(zhí)。

因此,請(qǐng)不要過(guò)分考慮-記住這一點(diǎn)。

保證行為

最后但并非最不重要的一點(diǎn)(這始終是正確的,但值得在此重復(fù)):

不要依靠無(wú)證的行為!

JLS不保證每個(gè)lambda評(píng)估都返回一個(gè)新實(shí)例(如上面的代碼所示)。 但是它不能保證觀察到的行為,即非捕獲的lambda總是由同一實(shí)例表示。 因此,不要編寫依賴于兩者的代碼。

不過(guò),我必須承認(rèn),這是一個(gè)艱難的過(guò)程。 認(rèn)真地說(shuō),誰(shuí)在使用某些功能之前先看過(guò)它們的JLS? 我當(dāng)然不會(huì)。

反射

我們已經(jīng)看到Java不能保證所評(píng)估的lambda表達(dá)式的身份。 盡管這是一個(gè)有效的優(yōu)化,但它可能會(huì)產(chǎn)生令人驚訝的效果。 為了防止這種情況引入細(xì)微的錯(cuò)誤,我們導(dǎo)出了以下準(zhǔn)則:

  • 使用默認(rèn)方法部分實(shí)現(xiàn)接口時(shí)要小心。
  • 不要在工廠方法中使用lambda表達(dá)式。
  • 當(dāng)身份很重要時(shí),請(qǐng)使用匿名類或更好的內(nèi)部類。
  • 依賴功能接口的標(biāo)識(shí)時(shí)要小心。
  • 最后, 不要依賴未記錄的行為!

翻譯自: https://www.javacodegeeks.com/2015/01/instances-of-non-capturing-lambdas.html

lambda捕獲this

總結(jié)

以上是生活随笔為你收集整理的lambda捕获this_非捕获Lambda的实例的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。