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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

非捕获Lambda的实例

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

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

總覽

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

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

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

功能界面

假設(shè)對于在構(gòu)建期間結(jié)果已經(jīng)存在的情況,我們需要對Future接口進(jìn)行特殊化。

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

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

一個(gè)工廠

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

未來工廠

/*** 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)造未來

最后,我們使用工廠創(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)行程序。 控制臺會說...

4? 不。 3。

WTF ?!

Lambda表達(dá)式的評估

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

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

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

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

[…]

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

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

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

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

JLS,Java SE 8版,§15.27.4

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

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

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

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

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

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

得到教訓(xùn)

因此,我們了解了為什么會這樣。 盡管這很有意義,但我仍然會說這種行為并不明顯,因此并不是每個(gè)開發(fā)人員都期望的。 這是產(chǎn)生錯(cuò)誤的溫床,因此讓我們嘗試分析情況并從中學(xué)習(xí)一些東西。

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

可以說,意外行為的根本原因是如何完善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)方法的文章,并且我打算再講一遍。 可以說,在做出決定時(shí)應(yīng)考慮此處提出的案例。

工廠中的Lambda

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

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

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

注意匿名類和lambda表達(dá)式的不同語義。 前者保證創(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á)式的自動轉(zhuǎn)換,反之亦然。 由于兩者之間存在細(xì)微的差異,這種看似純粹的句法轉(zhuǎn)換會帶來細(xì)微的行為變化。 (我最初并不了解。)

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

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

功能接口標(biāo)識

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

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

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

保證行為

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

不要依靠無證的行為!

JLS不保證每個(gè)lambda評估都返回一個(gè)新實(shí)例(如上面的代碼所示)。 但這并不能保證觀察到的行為,即未捕獲的lambda始終由同一實(shí)例表示。 因此,不要編寫依賴于任何一個(gè)的代碼。

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

反射

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

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

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

總結(jié)

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

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