Java 8 初探 - Closure

閱讀時間約 5 分鐘
此為過去的舊文,2014 年 3 月 30 日初次發表於 logdown。

印象中,在官方的《Java Magazine》雙月刊中,已經探討過好幾回的 Java 8 及 Lambda,但似乎鮮少討論到 Closure,稍微快速翻找一下,在 2013 的七八月號上有看到介紹 Lambda 的文章中討論到Closure。我想主要的原因應該是

  1. 在沒有 Lambda 前,anonymous class 本身就可以捕捉變數,只是捕捉的變數自動被視為 final。
  2. Java 的 Lambda 所捕獲的變數,某種程度還是不夠自由。無法完整重現自由變數,所以沒有特別去宣傳 Java Lambda 和 Closure 之間的關係。

翻了一下 Wikipedia 上對 Closure 的描述 (我想參考 Wikipedia 應該比參考某些專論programming languages的書要方便一些),在 Closure 內可以對自由變數的做任何變數上的操作,包含更動數值,這一點在許多語言的支援上也不見得完全相同。

以最近工作上常寫的 Objective C 來說,Apple 官方文件給了幾個例子,官方文件以儲存空間的角度去探討捕捉變數,這是對記憶體位置特別敏感的語言某種程度上的痛處,不過這裡就用比較抽象的方式解釋 code block 對於捕捉變數的處理。預設上,code block 捕捉變數的值,也就是說在捕捉後對變數的更動在 code block 內是看不見的,所以下例中 NSLog 所顯示的結果是 42 而不是 84

若希望 code block 捕捉變數而不是僅僅數值的話,需像下例在被捕捉的變數宣告上加上 __block 的修飾字,此時 NSLog 顯示的結果就會是 84,因為當 code block 執行時 (第6行),捕捉的變數 anInteger 已經變成 84 了 (第5行)。

__block 修飾字讓 code block 捕捉變數本身,所以也可以更動變數的值,因此下例中 NSLog 是在 callback() 執行後才顯示 anInteger 的值,結果是 100。我個人覺得這樣的處理有好有壞,就抽象程度上,額外需要 __block 修飾字讓工程師還是意識到記憶體位置的存在,這是降低語言的抽象程度(不直覺)。

Objective C 可用這些修飾字針對效能提升最佳化編譯結果,不但能同時提供唯讀/讀寫的捕捉變數,另外也提供編譯期間的檢查,例如沒用 __block 但卻在 code block 中變更變數值會視為錯誤,某種程度上我覺得是還不錯的設計。

好,該回到 Java 本身了,沒有 Lambda 前,anonymous class 可以像下例那樣捕捉 scope 中可見的變數 x,但最大問題是捕捉的變數 x 實際上是 final 變數 (effectively final),所以被註解的 x = 48; 若取消註解會被視為編譯錯誤。

即使改成用 Lambda 也是一樣的,如下例,在 Lambda 內 x 依舊是 final 變數,無法更動變數值,取消 x = 48; 的註解依然會是編譯錯誤。

Java 的 final 修飾字僅限制無法改變變數值,但若變數是個物件,呼叫物件 method 卻是允許的,即使該 method 會改變物件內的狀態都是允許的,所以 anonymous class 的範例可以改寫如下。同樣,取消 x = new AtomicInteger(48); 的註解會得到編譯錯誤,但用 x.set(48); 可以實際改變 x 的值 (這在 Objective C 也是一樣)。

所以,Lambda 的例子也可以改寫如下。很可惜,能夠 Autoboxing and Unboxing 的資料型態,例如:Integer,都是 immutable 的資料型態,使用上無法像 JavaScript 這類將基礎型別都視為物件的語言那樣方便。不過,某種程度上有點像自由變數了。

最後,Java 8 雖然支援 Lambda,但我覺得 Closure 某種程度上還不稱不上是 Java 的第一級居民,而且如Java 8 初探 - Lambda所述,為了方便測試,除非是只有一行或是非常簡單的程式碼 (too simple to break) 不用擔心測試的問題外,我還是比較喜歡寫一些小而易測的 class,而不是使用 Lambda,至於捕捉變數,透過建構子將變數帶入物件也是一種方式。至於什麼情況下會常寫只有一行或是非常簡單的 Lambda 呢?我覺得 Stream,新的 Collection API 開啟了相當大的可能性。

52會員
102內容數
這是從 Medium 開始的一個專題,主要是想用輕鬆閒談的方式,分享這幾年軟體開發的心得,原本比較侷限於軟體架構,但這幾年的文章不僅限於架構,也聊不少流程相關的心得,所以趁換平台,順勢換成閒談軟體設計。
留言0
查看全部
發表第一個留言支持創作者!
Spirit的沙龍 的其他內容
2024 四國行 Day 4
閱讀時間約 5 分鐘
2024 四國行 Day 5
閱讀時間約 3 分鐘
書摘《程式設計守則》
閱讀時間約 25 分鐘
書摘《從 0 到 1》
閱讀時間約 17 分鐘
書摘《高品質微服務》
閱讀時間約 11 分鐘
Java 8 初探 - Lambda
閱讀時間約 6 分鐘
你可能也想看
【Java】建立Maven專案使用第三方套件 相信很多初學者學python的原因,不外乎語法簡單、好上手、重點是有很多現成的套件可以玩。那麼,Java呢?有!當然有!而且還多到你不知道該選哪個好! 今天的文章主要示範如何在vscode新建立Java 的maven專案,並且透過maven安裝這些額外的套件(依賴)
Thumbnail
avatar
2023-09-02
【Java】安裝開始、開發環境建立 最近配合公司政策換了新電腦,重新回想起從頭建環境的惡夢。本篇文就來記錄一下如何開始踏入Java的第一步,方便起見也使用相對Eclipse、IntelliJ來說輕量不少的VScode作為編輯器。
Thumbnail
avatar
2023-07-05
Java Script自學經驗回顧來到學期2-3的階段,第一個作業就是打造餐廳清單。原本認為經過電影清單的學習經歷之後,對於打造餐廳清單應該也不會太過困難;沒想到我花了2個月的時間才把作業完整交出去。 在寫餐廳清單的初期,第一個碰到的問題就是首頁無法秀出餐廳評分這個選項。我試著參考其他同學的作品也改了版面的設計,卻始終無法出現餐廳評
avatar
奧莉薇
2023-02-28
Java Bird Coffee|通化街六張犁商圈,小巧而溫馨禾雀咖啡工作室位在通化街尾端,靠近六張犁的禾雀咖啡工作室,質樸的感覺是一家適合假日早晨來待上好一會兒的好去處。 主打自家烘培手沖及義式咖啡,最大特色還有自家製的麵包、甜點輕食,最喜歡吃的莫過於他們的鮮奶吐司。曾經住在這裡好長一段時間,週末去搶個一包可以在家裡烤來吃,是相當幸福的感覺。 冰羅馬咖啡則是我喝過最喜歡的
Thumbnail
avatar
Celine 寫在這裡
2023-02-06
Java abstract class 與 interface 的差別abstract class = 抽象類別 interface = 介面 抽象類別與介面都無法建立物件。 1. 使用abstract關鍵字來建立抽象類別,interface關鍵字建立介面。 interface只能繼承interface,且可以繼承多個:
Thumbnail
avatar
Vic Lin
2022-07-04
JAVA繼承篇繼承 在Java中,一個類可以由其他類派生。如果你要創建一個類,而且已經存在一個類具有你所需要的屬性或方法,那麼你可以將新創建的類繼承該類。 利用繼承的方法,可以重用已存在類的方法和屬性,而不用重寫這些代碼。被繼承的類稱為超類(super class),派生類稱為子類(subclass)。 繼承的特
Thumbnail
avatar
行至水窮處 坐看雲起時
2021-09-19
Java 編譯成 WebAssembly 的工具古早的年代想在網頁內埋 Java 還有 Java applet 可以用,在 Java applet 式微後,找來找去比較可以的辦法大概就是編譯成 WebAssembly 了吧! 想要把 Java 編譯成 WebAssembly,有下面三個工具可以選用
Thumbnail
avatar
Leon
2021-06-22
Java abstract class和interface的使用時機Java abstract class和interface的使用時機
Thumbnail
avatar
Vic Lin
2020-08-20
Java工程師:肉體的交易明細  給對想當Java工程師又興趣的人類,也許你是學生,又或是你正想轉職,我希望你們都不會因為未知而害怕,這條路沒有那麼困難。
Thumbnail
avatar
傑歐葛葛
2020-07-19