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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

lambda捕获this_非捕获Lambda的实例

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

lambda捕獲this

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

總覽

為了使這一點更有趣,我將以一個示例開頭,該示例將以我的個人WTF達到頂峰? 時刻。 完整的示例可以在專用的GitHub項目中找到 。

然后,我們將看到有關此意外行為的解釋,并最終得出一些結論以防止錯誤。

這里有個例子……它不是那么簡單或抽象,因為我希望它顯示這種情況的相關性。 但是從某種意義上說,這仍然是一個示例,它僅暗示實際上可能有用的代碼。

功能界面

假設對于在構建過程中已經存在結果的場景,我們需要對Future接口進行特殊化。

我們決定通過創建一個接口ImmediateFuture來實現此目的,該接口get()使用默認方法實現除get()之外的所有功能。 這導致功能界面 。

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

一個工廠

接下來,我們實現FutureFactory 。 它可能會創建各種期貨,但肯定會創建我們的新子類型。 它是這樣的:

未來工廠

/*** 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; }

創造未來

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

創建實例

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 ?!

運行程序。 控制臺會說...

4? 不。 3。

WTF ?!

Lambda表達式的評估

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

Lambda表達式的實例

理解這種行為的關鍵在于,事實是JRE不保證如何將lambda表達式轉換為相應接口的實例。 讓我們看一下Java語言規范對此事的看法:

15.27.4。 Lambda表達式的運行時評估

[…]

分配并初始化具有以下屬性的類的新實例,或者引用具有以下屬性的類的現有實例。

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

這些規則旨在通過以下方式為Java編程語言的實現提供靈活性:

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

JLS,Java SE 8版,§15.27.4

在其他優化中,這顯然使JRE可以返回相同的實例,以重復評估lambda表達式。

非捕獲Lambda表達式的實例

請注意,在上面的示例中,表達式不捕獲任何變量。 因此,它永遠不會因評估而改變。 而且由于lambda并非設計為具有狀態,因此不同的評估在其生命周期中也無法“分散”。 因此,一般而言,沒有充分的理由來創建多個不捕獲的lambda實例,因為它們在整個生命周期中都完全相同。 這樣可以使優化始終返回相同的實例。

(將其與捕獲某些變量的lambda表達式進行對比。對此表達式的直接評估是創建一個將捕獲的變量作為字段的類。每個評估都必須創建一個新實例,將捕獲的變量存儲在其字段中這些情況顯然并不普遍。)

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

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

得到教訓

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

使用默認方法進行子類型化

可以說,意外行為的根本原因是如何完善Future的決定。 為此,我們擴展了另一個接口,并使用默認方法實現了部分功能。 僅剩一個未實現的方法, ImmediateFuture成為了一個啟用lambda表達式的功能接口。

另外, ImmediateFuture可以是抽象類。 這樣可以防止工廠意外返回相同的實例,因為它不能使用lambda表達式。

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

工廠中的Lambda

由于lambda的引用相等性不可預測,因此工廠方法應仔細考慮使用它們來創建實例。 除非方法的協定明確允許不同的調用返回相同的實例,否則應完全避免使用它們。

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

匿名類與Lambda表達式

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

匿名類的替代實現

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

這尤其令人不安,因為許多IDE允許從匿名接口實現到lambda表達式的自動轉換,反之亦然。 由于兩者之間存在細微的差異,這種看似純粹的句法轉換會帶來細微的行為變化。 (我最初并不了解。)

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

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

功能接口標識

當您依賴功能接口的標識時要小心。 始終考慮是否有可能,無論您在何處獲得這些實例,都可能反復將您交給同一個實例。

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

因此,請不要過分考慮-記住這一點。

保證行為

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

不要依靠無證的行為!

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

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

反射

我們已經看到Java不能保證所評估的lambda表達式的身份。 盡管這是一個有效的優化,但它可能會產生令人驚訝的效果。 為了防止這種情況引入細微的錯誤,我們導出了以下準則:

  • 使用默認方法部分實現接口時要小心。
  • 不要在工廠方法中使用lambda表達式。
  • 當身份很重要時,請使用匿名類或更好的內部類。
  • 依賴功能接口的標識時要小心。
  • 最后, 不要依賴未記錄的行為!

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

lambda捕獲this

總結

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

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。