Kotlin 實戰範例
還是要先聲明,每個程式語言的誕生都是想要解決特定的問題,好壞與否其實關乎個人的選擇,這裡提及其他的 JVM 語言僅用來互相比較。
JVM 語言眾多,Kotlin 只是其中一種,藉由和其他 JVM 語言的比較可以瞭解 Kotlin 在設計上的哲學。Kotlin 做為一個新生代的程式語言,可以從許多程式語言上學到優秀的解決方法,讓語法更加靈活與現代化。
程式碼必須被編譯為機器語言讓電腦執行,針對不同的作業系統及硬體裝置都必須特別處理,因此當年為了解決這個問題而打造了 JVM (Java Virtual Machine, Java 虛擬機器),由它來處理不同作業系統及硬體裝置的部分,我們寫的程式只要編譯成位元碼 (byte code) 交由 JVM 來處理即可。
由於這個設計,只要其他程式語言可以編譯成 JVM 能讀取的位元碼,就能夠利用 JVM 來達到跨平台的能力,於是就產生了許多可以在 JVM 上運行的程式語言,可以看看 JVM 語言清單 。
在 Kotlin 之前比較熱門的 JVM 語言有 Scala 及 Groovy。Kotlin 受這兩種語言的啟發而被設計出來,細看這三種語言的語法,會有許多相似之處。 Groovy 在 2003 年發表,是個同時包含動、靜態型別系統的語言,受到 Java 、Python 及 Ruby 等語言的啟發,Groovy 就像是 JVM 版的 Python。Scala 也差不多在同一個時期出現,受啟發的語言眾多,語法中可以看到這些語言的影子。
Kotlin 接收了這些語言的設計哲學,並且將目標放在接近 Java 的編譯速度、檔案大小及方法數量上,這也許就是 Kotlin 被選為 Android 官方語言的原因之一,因為 Android 有方法數量上限的 64k 的限制 (現在可以透過 Multidex 來解決),如果使用 Scala 或 Groovy ,方法數量會爆增,很容易就超過這個限制,可以看看 SidneyXu/AndroidDemoIn4Languages 的實驗 (註:這個實驗是 2017 年做的,數據可能過時,請參考就好),該實驗以 4 種 JVM 語言 Java、Groovy、Scala、Kotlin 來做比較,請特別注意檔案大小及方法數量,除了 Kotlin 較接近 Java 以外,其他兩個都大上許多。
Kotlin 和 Java 的互相操作性做的很好,也就是說,我們可以在現有專案中漸進地使用 Kotlin,不必一次將原本的 Java 專案全部轉移成 Kotlin。要在 Java 中呼叫 Kotlin 的程式碼,或是反過來都沒問題,這樣的好處是,我們可以在 Kotlin 中使用所有已經存在的 Java 框架和類別庫,這對剛起步的 Kotlin 來說幫助非常大。
Kotlin 和 Java 在語法上雖然有不少差異,但是主要還是在思考方式的不同,Kotlin 在設計上積極地使程式碼變得簡潔,並且讓語意清楚,對於已經在使用 Java 的讀者,建議在寫 Kotlin 時暫時忘掉 Java ,當成初學者來學習,避免寫出很像 Java 的 Kotlin 程式碼。
在寫 Java 的時候,因為沒有明確的方法可以知道某個物件是否為空 (null),如果不小心使用到這個物件,就會引發空指標的例外錯誤 (NPE, NullPointerException) ,因此,在 Java 8 時加入了 Optional 的解決方案,不過這個功能在使用上並不是那麼優雅。Kotlin 在設計之初就考慮到這個問題,內建了 null 安全機制,在宣告變數時就必須指定該變數是否接受 null ,這樣在編譯階段就能提早發現並且優雅的處理。
Kotlin 是個混合型的程式語言,同時擁有物件導向及函數式程式設計的特性,它的函式是頭等函式 (first-class function),也就是說,一個函式可以被當成另外一個函式的參數傳入或回傳值傳出 (如果你會 Javascript 就能明白),而且函式不一定要寫在類別裡面,它可以單獨存在,再加上擴充函式這個非常棒的特性,在撰寫程式時可以非常靈活,以上這些特性在 Java 中是沒有的。
最後,如我們前面提過的,Kotlin 的編譯結果很接近 Java,並且已經被 Android 團隊列為官方的第一優先 (Kotlin-first) 開發語言,如果想開發 Android 應用程式,這已經是不用多想的必學程式語言;在其他領域如網頁框架及原生應用程式等等,這些更多更廣的應用都非常令人期待。
Java 必須先建立一個類別,並在其中建立一個靜態方法 main() 做為程式的起點,請看範例:
/* Java */ public class Hello { public static void main(String[] args) { System.out.print("Hello Java"); } }
Kotlin 不需要特別建立類別,它的函式是頭等函式,等級和類別一樣,因此可以直接以 main() 函式做為程式的起點,請看範例:
/* Kotlin */ fun main(args: Array<String>) { print("Hello Kotlin") }
Java 必須在敍述行的結尾加上分別,Kotlin 則不需要,除非將兩行敍述寫成一行,中間才需要加上分號,但是不建議這麼做。
詳細的內容會有專門的章節做介紹,這裡直接看範例:
/* Java */ public class Book { private String isbn; private String title; private String description; private int price; public String getIsbn() { return isbn; } public void setIsbn(String isbn) { this.isbn = isbn; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } }
相同的內容在 Kotlin 中只要一行,為了容易閱讀,將程式碼斷行排列,請看範例:
/* Kotlin */ data class Book( var isbn:String, var title:String, var description:String, var price:Int )
註: Java 14 預計會引進 Records ,其特性和 data class 很像。
使用未初始化的空物件會導致程式發生 NPE (NullPointerException) 的例外錯誤,於是我們會在程式碼中不斷地到處檢查物件是否為 null。
Java 8 新增了一個 Optional 的新功能來避免 NPE,而 Kotlin 則內建在資料型別中,以問號 ? 來表示可能為空值的情況,請看範例:
/* Java */ import java.util.Optional; public class App { public static void main(String[] args) { String name = null; print(name); // 使用 Java 8 的 Optional Optional<String> optName = Optional.ofNullable(name); print(optName); } private static void print(String name) { // 傳統寫法 if (name != null) { System.out.println(name); } } private static void print(Optional<String> optName) { // 和傳統寫法差不多,只是換成呼叫方法 if (optName.isPresent()) { // 必須呼叫 get() 方法取值 System.out.println(optName.get()); } // 用這個可以少寫 if optName.ifPresent( name -> System.out.println(name)); // 或是在發生 null 時給其他的值 System.out.println(optName.orElse("is null")); } }
註:若要瞭解 Java 8 Optional 的完整用法,可以參考: Java8 新功能筆記 (4) - Optional 。
在 Optional 之前,由於方法中無法得知傳進來的引數值是不是 null,一般來說都會防衛式的去檢查;在使用 Optional 後,我們可以明確得知傳入的引數值可能 null,因此做出額外的檢查及處理。現在來看看 Kotlin 怎麼做:
/* Kotlin */ fun main(args: Array<String>) { // 這樣會編譯錯誤 // var name: String = null var name: String? = null // 編譯錯誤,這個函式不接受可能 null 的變數傳入 // printName(name) var name2: String = "Tony" printName(name2) } fun printName(name: String) { println(name) }
Kotlin 在設計型別時就加上 null 判斷的機制,因此不需要在程式碼上做額外的操作,不僅簡潔也更明確,而且這些全都在編譯時期就能檢查出來。
:speech_balloon: Kotlin 是開源且免費的嗎?
是。使用 Apache 2.0 license 授權,原始碼在 GitHub 。
:speech_balloon: Kotlin 是屬於物件導向還是函數式程式語言?
同時擁有兩者特性,可以同時混用物件導向及函數式的語法。
:speech_balloon: 使用 Kotlin 勝過 Java 的優點是什麼?
簡潔。Kotlin 官方粗估,相同功能的程式碼行數約可較 Java 減少 40%。型別系統內建非 null 型別的支援,大大增加型別安全及減少 NPE 的發生。還有其他從函數式程式語言學習而來的功能。
:speech_balloon: Kotlin 是否相容 Java?
是的。Kotlin 和 Java 程式碼可以完全互相操作,無縫地在 Kotlin 中呼叫 Java 程式碼,或反過來。
:speech_balloon: 哪些類型應用可以使用 Kotlin ?
除了 Android 以外,包含網站伺服端 (Server)、前端 (Javascript) 、桌面應用 (JavaFx) 及正在開發的原生應用 (Kotlin/Native) 還有資料科學等等。
Kotlin 的許多語言特性都不是原創,而是借鏡其他語言的優點,並且設計的更好用。程式碼簡潔、語意清 、容易閱讀,就能減少犯錯的機會,對比 Java 來說可以提高不少生產力,Kotlin 是值得一學的程式語言。
下篇預告:Kotlin 實戰範例 (2) 基礎 (變數、型別)
完整內容可以參考電子書: Google Play Pubu