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 開啟了相當大的可能性。

avatar-img
53會員
104內容數
這是從 Medium 開始的一個專題,主要是想用輕鬆閒談的方式,分享這幾年軟體開發的心得,原本比較侷限於軟體架構,但這幾年的文章不僅限於架構,也聊不少流程相關的心得,所以趁換平台,順勢換成閒談軟體設計。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
Spirit的沙龍 的其他內容
Java 8 終於在 2014 的 3 月 18 日正式釋出了,不過自從用 Objective C 開發 iOS App後,我已經有好一陣子沒碰 Java,期間曾經有短暫寫一點點,但卻沒有時間去用 beta 版的 Java 8,直到最近才又開始玩一下。
內容十分精實,一百多頁很薄的一本書,但含了很多有用的資訊,就算不是開發微服務,書中的內容也可以用在很多雲端服務的開發與維運上。中文版唯一可惜的地方,翻譯非常不通順,很多不像中文的句子,會看到好幾個「與」連在一起用,標點符號的用法也有點怪,閱讀的痛苦指數有點高...
3/5高品質微服務
今天的任務是找到獨一無案的方法創造新事物,不只讓未來變得不一樣,而且要更好,所以我們要從 0 到 1。最重要的第一步是自己獨立思考。唯有重新看待世界,像古人首次見到它那樣覺得新鮮古怪,我們才能重新創造,並將更好的未來留給後世。
5/5從 0 到 1
這陣子比較有空可以去天瓏書局晃晃,正好看到這本剛上市不久的書,整體上大多數守則,也是我自己一直在遵循的,是相當不錯的一本總結書。但真的要仔細看每一節的內容,理解每個原則背後的情境與想要改善的問題是什麼。如果只是把每一節的標題拿來使用,很容易就會發現衝突的部分。
5/5程式設計守則
今天完全沒有行程,就只有去機場搭飛機回台灣,飯店的自助式早餐依舊是很豐盛,甚至還有一區是可以自由組裝海鮮丼,我沒裝飯,只拿了幾樣海鮮。飛機是 11:05 起飛,所以,雖然離機場很近,提早兩小時到機場,意味著九點多該到機場了。
行程簡述 天空海盧飯店 -> 小豆島土庄港 -> 四國高松港 -> 栗林公園 -> 金刀比羅宮參道午餐 -> 道後溫泉車站、本館和商店街 -> 彩朝樂
Java 8 終於在 2014 的 3 月 18 日正式釋出了,不過自從用 Objective C 開發 iOS App後,我已經有好一陣子沒碰 Java,期間曾經有短暫寫一點點,但卻沒有時間去用 beta 版的 Java 8,直到最近才又開始玩一下。
內容十分精實,一百多頁很薄的一本書,但含了很多有用的資訊,就算不是開發微服務,書中的內容也可以用在很多雲端服務的開發與維運上。中文版唯一可惜的地方,翻譯非常不通順,很多不像中文的句子,會看到好幾個「與」連在一起用,標點符號的用法也有點怪,閱讀的痛苦指數有點高...
3/5高品質微服務
今天的任務是找到獨一無案的方法創造新事物,不只讓未來變得不一樣,而且要更好,所以我們要從 0 到 1。最重要的第一步是自己獨立思考。唯有重新看待世界,像古人首次見到它那樣覺得新鮮古怪,我們才能重新創造,並將更好的未來留給後世。
5/5從 0 到 1
這陣子比較有空可以去天瓏書局晃晃,正好看到這本剛上市不久的書,整體上大多數守則,也是我自己一直在遵循的,是相當不錯的一本總結書。但真的要仔細看每一節的內容,理解每個原則背後的情境與想要改善的問題是什麼。如果只是把每一節的標題拿來使用,很容易就會發現衝突的部分。
5/5程式設計守則
今天完全沒有行程,就只有去機場搭飛機回台灣,飯店的自助式早餐依舊是很豐盛,甚至還有一區是可以自由組裝海鮮丼,我沒裝飯,只拿了幾樣海鮮。飛機是 11:05 起飛,所以,雖然離機場很近,提早兩小時到機場,意味著九點多該到機場了。
行程簡述 天空海盧飯店 -> 小豆島土庄港 -> 四國高松港 -> 栗林公園 -> 金刀比羅宮參道午餐 -> 道後溫泉車站、本館和商店街 -> 彩朝樂
你可能也想看
Google News 追蹤
Thumbnail
本章節的目的是介紹Java中的套件使用,包括如何引用第三方套件和自定義模組,如何創建和使用自定義套件,以及介紹一些常見的Java標準庫套件。這些內容將幫助讀者更好地理解和使用Java的套件系統。
Thumbnail
本章節旨在介紹Java程式語言中的「例外處理」概念。透過各個小節,讀者將學習到何謂例外處理、為何要使用它、如何在Java中實現例外處理,以及如何正確地捕獲和處理各種類型的異常。此外,本章節還提供了如何主動觸發異常,以及如何創建和使用自定義異常的實例。
Thumbnail
本章節是Java入門的第八天,主要介紹物件導向的概念。這包括了類別、建構子、存取修飾子、繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、Lambda表達式、泛型和反射等主題。每個主題都配有相關的程式碼範例,以協助讀者更好地理解這些概念。
Thumbnail
本章節主要介紹Java語言中的函數(也稱為方法)的使用,包括函數的基本結構、函數表達式(Lambda表達式)、箭頭函數、匿名函數的使用,以及如何呼叫函數、如何使用函數參數和函數的返回值等內容。通過學習本章節,讀者將能夠熟練掌握Java語言中的函數相關知識,並能夠在實際編程中靈活運用。
Thumbnail
此章節旨在介紹Java程式語言中的各種資料型別,包括基本型別、引用型別、集合型別、陣列型別、字典型別等。它還講解了如何在Java中進行型別轉換和自定義型別,並提供了相關的程式碼示例。
Thumbnail
此章節旨在介紹Java的基本語法、註解和變數的使用。透過學習,讀者將了解Java程式的基本結構、程式進入點的定義、如何撰寫單行和多行註解,以及如何宣告和初始化變數。
Thumbnail
這篇文章的目的是對Java程式設計語言進行介紹,包括它的特性、應用範疇、主要使用者,以及相關的學習資源和常見的庫與框架。此外,它也提供了一些學習Java的渠道,以及與Java相關的其他知識。
Thumbnail
本章節旨在介紹JavaScript中的物件導向編程。內容包括類別(Class)的定義和使用,建構子的作用,以及公開,私有,受保護(Protected)等不同訪問修飾符的概念。此外,還涵蓋了繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda表達式、泛型、反射等物件導向的主要觀念。
Thumbnail
在本章節中,我們將學習JavaScript的基本語法,包括如何註解代碼和如何聲明變數。瞭解這些基礎知識對於進一步學習和使用JavaScript來編寫代碼是非常重要的。
Thumbnail
如果你曾經撰寫過網頁,那你一定接觸過 JavaScript 無論是在 NodeJs 或是瀏覽器中運行。 但你有沒有想過,我們寫下的 JS 程式碼,這些看似單純的英文和符號,是如何被轉化為機器能夠理解和執行的程式呢? 今天,讓我們一起深入了解其中的核心主角 ——Google 開發的開源 Java
Thumbnail
本章節的目的是介紹Java中的套件使用,包括如何引用第三方套件和自定義模組,如何創建和使用自定義套件,以及介紹一些常見的Java標準庫套件。這些內容將幫助讀者更好地理解和使用Java的套件系統。
Thumbnail
本章節旨在介紹Java程式語言中的「例外處理」概念。透過各個小節,讀者將學習到何謂例外處理、為何要使用它、如何在Java中實現例外處理,以及如何正確地捕獲和處理各種類型的異常。此外,本章節還提供了如何主動觸發異常,以及如何創建和使用自定義異常的實例。
Thumbnail
本章節是Java入門的第八天,主要介紹物件導向的概念。這包括了類別、建構子、存取修飾子、繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、Lambda表達式、泛型和反射等主題。每個主題都配有相關的程式碼範例,以協助讀者更好地理解這些概念。
Thumbnail
本章節主要介紹Java語言中的函數(也稱為方法)的使用,包括函數的基本結構、函數表達式(Lambda表達式)、箭頭函數、匿名函數的使用,以及如何呼叫函數、如何使用函數參數和函數的返回值等內容。通過學習本章節,讀者將能夠熟練掌握Java語言中的函數相關知識,並能夠在實際編程中靈活運用。
Thumbnail
此章節旨在介紹Java程式語言中的各種資料型別,包括基本型別、引用型別、集合型別、陣列型別、字典型別等。它還講解了如何在Java中進行型別轉換和自定義型別,並提供了相關的程式碼示例。
Thumbnail
此章節旨在介紹Java的基本語法、註解和變數的使用。透過學習,讀者將了解Java程式的基本結構、程式進入點的定義、如何撰寫單行和多行註解,以及如何宣告和初始化變數。
Thumbnail
這篇文章的目的是對Java程式設計語言進行介紹,包括它的特性、應用範疇、主要使用者,以及相關的學習資源和常見的庫與框架。此外,它也提供了一些學習Java的渠道,以及與Java相關的其他知識。
Thumbnail
本章節旨在介紹JavaScript中的物件導向編程。內容包括類別(Class)的定義和使用,建構子的作用,以及公開,私有,受保護(Protected)等不同訪問修飾符的概念。此外,還涵蓋了繼承、多型、封裝、介面、抽象類別、靜態類別、列舉、委派、Lambda表達式、泛型、反射等物件導向的主要觀念。
Thumbnail
在本章節中,我們將學習JavaScript的基本語法,包括如何註解代碼和如何聲明變數。瞭解這些基礎知識對於進一步學習和使用JavaScript來編寫代碼是非常重要的。
Thumbnail
如果你曾經撰寫過網頁,那你一定接觸過 JavaScript 無論是在 NodeJs 或是瀏覽器中運行。 但你有沒有想過,我們寫下的 JS 程式碼,這些看似單純的英文和符號,是如何被轉化為機器能夠理解和執行的程式呢? 今天,讓我們一起深入了解其中的核心主角 ——Google 開發的開源 Java