Clojure 寄生於 Java 之中,汲取它的養分並試圖解放它的繁重。Java 有優秀的即時編譯 (Just-in-time compilation) 功能、垃圾資源回收 (Garbage collection)、HotSpot 虛擬機器與傑出的位元組程式碼。寄宿在其上的語言,不需自行實現便可以擁有強大的武器爲後援。
既然寄宿在 Java 之上,不能只理解 Clojure,而對 Java 視而不見。了解 Java 是認識平台、優秀的生態圈與工具,讓程式邁向卓越的方法。
本篇文章會從 Clojure 方呼叫 Java,以及 Java 方呼叫 Clojure 兩方面,爲各位介紹如何與 Java 共同合作。
Clojure 中載入 Java 套件庫的方法在先前的文章也有提到過,就是使用 import
載入 Java 函式庫:
(import java.util.Date)
(Date.)
;; => #inst "2017-12-31T15:51:54.105-00:00"
以上載入 java.util.Date
套件到目前的命名空間後,就可以順利使用 Date 類別。你也可以同時載入同個套件內的不同類別:
(import java.util.Date java.util.Calendar)
;; => java.uti.Calendar
也可以使用下面範例載入同個套件下的類別:
(import '(java.util Date Calendar)
'(java.net URI ServerSocket))
;; => java.net.ServerSocket
Clojure 使用 new
特殊形式 (Special form) 來創建 Java 類別的執行個體 (Instance),第一個參數是類別名稱,接著是該類別建構式的各個參數,返回值爲該類別的執行個體:
(def date (new java.util.Date))
date
;; => #inst "2017-12-31T17:10:35.227-00:00"
Clojure 提供了語法糖 (Syntactic Sugar),用來簡化頻繁輸入 new
,只要在類別名稱之後加上點 (.) 即可:
(String. "Hello")
;; => "Hello"
由於 Clojure 中的字串即是 Java 的字串類別 java.util.String,可以在 java.util.String 類別上使用的方法 (Method),都可以在 Clojure 字串使用,例如將文字改成大寫與小寫:
(. "Hello World" toUpperCase)
;; => “HELLO WORLD”
(. "Hello World" toLowerCase)
;; => “hello world”
使用方式就是在列表中的第一個位置放上點 (.),依序再放上執行個體 (Instance) 以及方法的名稱,如果還有參數則再依序放上方法的參數:
(. "Hello World" indexOf "ello")
;; => 1
Clojure 也爲此提供了語法糖,只要將方法名稱放在列表的第一個位置,並在方法名稱之前加上點 (.),之後列表的位置依序放入執行個體以及方法的參數即可:
(.toUpperCase "Hello World")
;; => “HELLO WORLD”
(.indexOf "Hello World" "ello")
;; => 1
靜態方法 (Static method) 以及靜態欄位 (Static field) 可以使用如上用點的方式呼叫,也可以使用語法糖 Class/Method 的方式:
(. java.lang.Math PI)
;; => 3.141592653589793
java.lang.Math/PI
;; => 3.141592653589793
還有可以簡化層層內嵌運算式的特殊形式,即是在運算式的第一個位置,放上兩個點符號 (.),用來將以下的範例簡化:
(. (. (. " Hello " trim) toUpperCase) length)
;; => 5
以上範例先將字串前後空白去除,再將字串轉成大寫,之後算出字串的長度。最裡頭的運算式求值之後取得新的執行個體,再傳遞給下一個運算式求值,Clojure 則提供了兩個點符號的特殊形式,將內嵌多層的運算式變得平坦,更易寫、易讀和易於理解:
(.. " Hello" trim toUpperCase length)
;; => 5
(未完待續)