Kotlin 實戰範例 (1) 為什麼要學 Kotlin

Kotlin 實戰範例

Java 寫的好好的,為什麼要改用 Kotlin 呢?Kotlin 提供了哪些 Java 沒有的語言特性?這些特性如何讓我們寫程式更順手、更開心呢?

還是要先聲明,每個程式語言的誕生都是想要解決特定的問題,好壞與否其實關乎個人的選擇,這裡提及其他的 JVM 語言僅用來互相比較。

JVM 語言眾多,Kotlin 只是其中一種,藉由和其他 JVM 語言的比較可以瞭解 Kotlin 在設計上的哲學。Kotlin 做為一個新生代的程式語言,可以從許多程式語言上學到優秀的解決方法,讓語法更加靈活與現代化。

1.1 眾多的 JVM 語言

程式碼必須被編譯為機器語言讓電腦執行,針對不同的作業系統及硬體裝置都必須特別處理,因此當年為了解決這個問題而打造了 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 以外,其他兩個都大上許多。

1.2 Kotlin 的優點

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 應用程式,這已經是不用多想的必學程式語言;在其他領域如網頁框架及原生應用程式等等,這些更多更廣的應用都非常令人期待。

1.3 看看 Kotlin 如何做

1.3.1 程式的起點

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")
}

1.3.2 結尾的分號

Java 必須在敍述行的結尾加上分別,Kotlin 則不需要,除非將兩行敍述寫成一行,中間才需要加上分號,但是不建議這麼做。

1.3.3 資料類別

詳細的內容會有專門的章節做介紹,這裡直接看範例:

/* 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 很像。

1.3.4 安全的 null

使用未初始化的空物件會導致程式發生 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 判斷的機制,因此不需要在程式碼上做額外的操作,不僅簡潔也更明確,而且這些全都在編譯時期就能檢查出來。

1.4 Kotlin 快問快答

💬 Kotlin 是開源且免費的嗎?
是。使用 Apache 2.0 license 授權,原始碼在 GitHub

💬 Kotlin 是屬於物件導向還是函數式程式語言?
同時擁有兩者特性,可以同時混用物件導向及函數式的語法。

💬 使用 Kotlin 勝過 Java 的優點是什麼?
簡潔。Kotlin 官方粗估,相同功能的程式碼行數約可較 Java 減少 40%。型別系統內建非 null 型別的支援,大大增加型別安全及減少 NPE 的發生。還有其他從函數式程式語言學習而來的功能。

💬 Kotlin 是否相容 Java?
是的。Kotlin 和 Java 程式碼可以完全互相操作,無縫地在 Kotlin 中呼叫 Java 程式碼,或反過來。

💬 哪些類型應用可以使用 Kotlin ?
除了 Android 以外,包含網站伺服端 (Server)、前端 (Javascript) 、桌面應用 (JavaFx) 及正在開發的原生應用 (Kotlin/Native) 還有資料科學等等。

1.5 總結

Kotlin 的許多語言特性都不是原創,而是借鏡其他語言的優點,並且設計的更好用。程式碼簡潔、語意清𥇦、容易閱讀,就能減少犯錯的機會,對比 Java 來說可以提高不少生產力,Kotlin 是值得一學的程式語言。


繼續閱讀:Kotlin 實戰範例 (2) 基礎 (變數、型別)

完整內容可以參考電子書:Google Play Pubu 樂天 Kobo



本文網址:https://blog.tonycube.com/2020/07/kotlin-by-example-1-why-kotlin.html
Tony Blog 撰寫,請勿全文複製,轉載時請註明出處及連結,謝謝 😀

我要留言

留言小提醒:
1.回覆時間通常在晚上,如果太忙可能要等幾天。
2.請先瀏覽一下其他人的留言,也許有人問過同樣的問題。
3.程式碼請先將它編碼後再貼上。(線上編碼:http://bit.ly/1DL6yog)
4.文字請加上標點符號及斷行,難以閱讀者恕難回覆。
5.感謝您的留言,您的問題也可能幫助到其他有相同問題的人。