1. 程式人生 > >Kotlin初體驗

Kotlin初體驗

限制 層次 聲明 null target i-o b2c tel 暴露

讓我們從一個小例子開始,來看看 Kotlin 代碼長什麽樣子。這個例子定義了一個 Person 類來表示“人”,創建一個“人”的集合,查找其中年紀最大的人,並打印結果。盡管這是非常小的一段代碼,從中也可以看到 Kotlin 許多有趣的特性。
  我們對其中的一些特性做了標記,以便你可以方便地在本書後續的內容中找到它們。
  代碼簡要地進行了解釋,但是如果有些內容你現在還無法理解,請不要擔心,稍後我們會詳細討論。
  如果你想嘗試運行這個例子,最簡單的方法是使用 http://try.kotl.in 的在線 Playground。輸入示例代碼並單擊 Run 按鈕,代碼將會執行。
                 代碼清單 1.1 Kotlin 初體驗
技術分享


  你聲明了一個簡單的數據類,它包括了兩個屬性:name和age。age屬性默認為null(如果沒有指定)。在創建“人”的列表時,你省略了Alice的年齡,所以這裏年齡使用了默認值null。然後你調用了maxBy函數來查找列表中年紀最大的那個“人”。傳遞給這個函數的lambda表達式需要一個參數,使用it作為這個參數的默認名稱。如果age屬性為null,Elvis運算符(?:)會返回零。因為Alice的年齡沒有指定,Elvis運算符使用零代替了它,所以Bob幸運地成了年紀最大的人。
  喜歡這樣的代碼嗎?繼續讀下去,你將會學習到更多,並成為一名 Kotlin 專家。我們希望不久之後,在你自己的項目中也能看到這樣的代碼,而不只是在書上。

Kotlin的主要特征

  你大概已經知道了 Kotlin 是一種怎樣的語言,讓我們更加深入地了解一下它的關鍵屬性。首先,我們來看看你能用 Kotlin 創造哪些種類的應用程序。

目標平臺 :服務器端、Android 及任何 Java 運行的地方

  Kotlin 的首要目標是提供一種更簡潔、更高效、更安全的替代 Java 的語言,並且適用於現今使用 Java 的所有環境。Java 是一門非常受歡迎的語言,它廣泛地應用於不同的環境 :小到智能卡 (JavaCard 技術 ),大到 Google、Twitter、LinkedIn 和其他這種規模的互聯網公司運行的最大的數據中心。在這些地方,使用 Kotlin 可以幫助開發者在實現目標的同時減少代碼並避免麻煩。
  Kotlin 最常見的應用場景有 :

  • 編寫服務器端代碼(典型的代表是 Web 應用後端)
  • 創建 Android 設備上運行的移動應用

但 Kotlin 還有其他用武之地。例如,可以使用 Intel Multi-OS Engine((https:// software.intel.com/en-us/multi-os-engine)讓 Kotlin 代碼運行在 iOS 設備上。還可以使用 Kotlin 和 TornadoFX(https://github.com/edvin/tornadofx)以及 JavaFX(http://mng.bz/500y) 一起來構建桌面應用程序。
  除了 Java 之外,Kotlin 還可以編譯成 JavaScript,允許你在瀏覽器中運行 Kotlin 代碼。但截止本書撰寫時,對 JavaScript 的支持仍在 JetBrains 內部探索並進行原型開發,這超出了本書的範圍,而其他一些平臺也在考慮支持 Kotlin 的未來版本。
  正如你所看到的,Kotlin 的目標平臺是相當廣泛的。Kotlin 並沒有被限制在單一的問題域,也沒有被限制在解決軟件開發者面臨的某一類型的挑戰。相反,對所有開發過程中湧現的任務,Kotlin 都提供了全面的生產力提升。它借助支持特定領域或編程範式的庫,提供了卓越的集成水準。接下來讓我們來看看 Kotlin 作為一種編程語言的關鍵特質。

靜態類型

  Kotlin 和 Java 一樣是一種靜態類型的編程語言。這意味著所有表達式的類型在編譯期已經確定了,而編譯器就能驗證對象是否包含了你想訪問的方法或者字段。
  這與動態類型的編程語言形成了鮮明的對比,後者在 JVM 上的代表包括 Groovy 和 JRuby。這些語言允許你定義可以存儲任何數據類型的變量,或者返回任何數據類型的函數,並在運行時才解析方法和字段引用。這會減少代碼量並增加創建數據結構的靈活性。但它的缺點是,在編譯期不能發現像名字拼寫錯誤這樣的問題,繼而導致運行時的錯誤。
  另一方面,與 Java 不同的是,Kotlin 不需要你在源代碼中顯式地聲明每個變量的類型。很多情況下,變量類型可以根據上下文來自動判斷,這樣就可以省略類型聲明。這裏有一個可能是最簡單的例子 :

val x = 1

  在聲明這個變量時,由於變量初始化為整型值,Kotlin 自動判斷出它的類型是Int。編譯器這種從上下文推斷變量類型的能力被稱作類型推導。
  下面羅列了一些靜態類型帶來的好處 :

  • 性能——方法調用速度更快,因為不需要在運行時才來判斷調用的是哪個方法。
  • 可靠性——編譯器驗證了程序的正確性,因而運行時崩潰的概率更低。
  • 可維護性——陌生代碼更容易維護,因為你可以看到代碼中用到的對象的類型。
  • 工具支持——靜態類型使 IDE 能提供可靠的重構、精確的代碼補全以及其他特性。

得益於 Kotlin 對類型推導的支持,你不再需要顯式地聲明類型,因此大部分關於靜態類型的額外冗長代碼也就不復存在了。
  當你檢視 Kotlin 類型系統的細節時,你會發現許多熟悉的概念。類、接口以及泛型和 Java 非常接近,所以大部分的 Java 知識可以很容易地轉移到 Kotlin。然而,也會有一些新概念出現。
  其中最重要的概念是 Kotlin 對可空類型的支持,通過在編譯期檢測可能存在的空指針異常,它讓你可以寫出更可靠的程序。
  另一個 Kotlin 類型系統的新概念是對函數類型的支持。要搞清楚這一點,我們先要了解函數式編程的主要思想,以及 Kotlin 是如何支持這種編程風格的。

函數式和面向對象

  作為一個 Java 開發者,你一定對面向對象編程的核心概念爛熟於胸,但函數式編程對你來說卻可能很新鮮。函數式編程的核心概念如下 :

  • 頭等函數——把函數(一小段行為)當作值使用,可以用變量保存它,把它當作參數傳遞,或者當作其他函數的返回值。
  • 不可變性——使用不可變對象,這保證了它們的狀態在其創建之後不能再變化。
  • 無副作用——使用的是純函數。此類函數在輸入相同時會產生同樣的結果,並且不會修改其他對象的狀態,也不會和外面的世界交互。

函數式編程風格的代碼能給你帶來什麽好處 ? 首先,簡潔。函數式風格的代碼 比相應的命令式風格的代碼更優雅、更簡練,因為把函數當作值可以讓你獲得更強 大的抽象能力,從而避免重復代碼。
  假設你有兩段類似的代碼,實現相似的任務(例如,在集合中尋找一個匹配的 元素)但具體細節略有不同(如何判斷元素是匹配的)。可以輕易地將這段邏輯中公 共的部分提取到一個函數中,並將其他不同的部分作為參數傳遞給它。這些參數本身也是函數,但你可以使用一種簡潔的語法來表示這些匿名函數,它被稱作 lambda 表達式 :
技術分享
  函數式編程風格的代碼帶來的第二個好處是多線程安全。多線程程序中最大的錯誤來源之一就是,在沒有采用適當同步機制的情況下,在不同的線程上修改同一份數據。如果你使用的是不可變數據結構和純函數,就能保證這樣不安全的修改根 本不會發生,也就不需要考慮為其設計復雜的同步方案。
  最後,函數式編程意味著測試更加容易。沒有副作用的函數可以獨立地進行測試,因為不需要寫大量的設置代碼來構造它們所依賴的整個環境。
  一般來說,函數式編程風格可以在任何編程語言中使用(包括 Java),它的很多主張都被認為是良好的編程風格。然而並不是所有的語言都提供了語法和庫支持,讓我們可以毫不費力地使用這種風格。例如,Java 8 之前的 Java 版本都缺少了這種支持。Kotlin 擁有豐富的特性集從一開始就支持函數式編程風格,包括 :

  • 函數類型,允許函數接受其他函數作為參數,或者返回其他函數。
  • lambda 表達式,讓你用最少的樣板代碼方便地傳遞代碼塊
  • 數據類,提供了創建不可變值對象的簡明語法
  • 標準庫中包括了豐富的 API 集合,讓你用函數式編程風格操作對象和集合

Kotlin 允許你使用函數式編程風格但並沒有強制你使用它。當你需要的時候,可以使用可變數據,也可以編寫帶副作用的函數,而且不需要跳過任何多余的步驟。然後,毫無疑問的是,在 Kotlin 中使用基於接口和類層次結構的庫就像 Java 一樣簡單。當編寫 Kotlin 代碼的時候,可以結合使用面向對象編程和函數式編程風格,並使用最合適的工具來對付亟待解決的問題。

免費並開源

  Kotlin 語言(包括編譯器、庫和所有相關工具)是完全開源的,並且可以自由使用。它采用 Apache 2 許可證 ;其開發過程完全公開在 GitHub (http://github.com/jetbrains/ kotlin) 上,並且歡迎來自社區的貢獻。如果你要開發 Kotlin 應用程序,有三種開源 IDE 供你選擇 :IntelliJ IDEA Community2 版、Android Studio 以及 Eclipse,它們都完 全支持 Kotlin(當然,IntelliJ IDEA Ultimate 也支持 Kotlin。)
現在你明白了 Kotlin 是什麽語言,讓我們看看 Kotlin 在具體的實際應用中會給你帶來哪些好處。

Kotlin應用

  如前所述,Kotlin 使用的兩個主要的領域是服務器端和 Android 開發。接下來我們分別看看這兩個領域,以及為什麽 Kotlin 非常適合它們。

服務器端的 Kotlin

  服務器端編程是一個非常大的概念,它包含了所有下列的應用程序類型甚至更多 :

  • 返回 HTML 頁面給瀏覽器的 Web 應用程序
  • 通過 HTTP 暴露 JSON API 的移動應用後端服務
  • 通過 RPC 協議互相通信的微服務

多年以來,開發者一直在構建這些類型的應用,並且積累了大量的框架和技術來幫助他們構建這些應用。這些應用通常並不是孤立地開發或者從零開始的,它們幾乎總是對現有的系統進行擴展、改進或者替換,新的代碼必須和系統中現有部分進行集成,而這些部分可能很多年之前就寫成了。
  這種環境下 Kotlin 的一大優勢就是它與現有的 Java 代碼無縫的互操作性。無論是要編寫一個全新的組件還是移植一個現有服務的代碼,Kotlin 都毫無壓力。不管你需要在 Kotlin 中繼承 Java 類,還是以某種方式註解一個類的方法或字段,都不會遇到任何問題。它帶來的優點是系統的代碼會更緊湊、更可靠、更易於維護。
  與此同時,Kotlin 還引入了許多用於開發這類系統的新技術。例如,對構建器模式的支持讓你可以使用更簡潔的語法來創建任何對象圖,同時保留了語言中全套的抽象機制和代碼重用工具。
  這個特性的一個最簡單的用例就是 HTML 生成庫,它可以把一個外部模板語言替換成簡潔且完全類型安全的解決方案。這裏有一個例子 :
技術分享
  可以輕松地把映射到HTML標簽的函數和常規的Kotlin語言結構組合起來。你不再需要使用一門獨立的模板語言,也不需要學習新的語法,僅僅使用循環就可以生成HTML頁面。
  另一個能用上Kotlin幹凈和簡潔的DSL的用例是持久化框架。例如,Exposed 框架(https://github.com/jetbrains/exposed)就提供了易讀的 DSL,可以完全使用 Kotlin 代碼來描述 SQL 數據庫的結構並執行查詢操作,並且有全面的類型檢查。下面這個小例子展示了可行的做法 :
技術分享

Android 上的 Kotlin

  一個典型的移動應用和一個典型的企業應用完全不同。它更小,更少地依賴與現有的代碼集成,通常需要快速交付,同時需要保證在大量的設備上能夠可靠地運行。這類項目 Kotlin 也能勝任。
  Kotlin 的語言特性,加上支持 Android 框架的特殊編譯器插件,讓 Android 的開發體驗變得高效和愉悅。常見的開發任務,比如給控件添加監聽器或是把布局元 素綁定到字段,可以用更少的代碼完成,有時甚至根本不用寫任何代碼(編譯器會幫你生成)。同樣由 Kotlin 團隊打造的庫 Anko(https://github.com/kotlin/anko)給許多標準 Android API 添加了 Kotlin 友好的適配器,進一步提升了 Android 的開發體驗。
  下面是 Anko 的一個簡單例子,可以品嘗到使用 Kotlin 進行 Android 開發的滋味。只要把這段代碼放在一個 Activity 中,一個簡單的 Android 應用就做好了!
技術分享
  使用Kotlin帶來的另一優勢就是更好的應用可靠性。如果你有開發Android應用的經驗,你一定對“Unfortunately, Process Has Stopped”對話框深惡痛絕。如果你的應用有未處理的異常,這個對話框就會出現,而這種異常一般是NullPointerException(空指針異常)。Kotlin的類型系統通過精確地跟蹤null值,大大減輕了空指針異常問題帶來的壓力。大部分Java中會導致NullPointerException的代碼在Kotlin中無法編譯成功,以確保這些錯誤在應用到達用戶手中之前得到修正。
  同時,由於 Kotlin 完全兼容 Java 6,使用它並不會帶來任何新的編譯問題。你可以享受所有 Kotlin 的酷炫新特性,而你的用戶仍然可以在他們的設備上使用你的應用,即使他們的設備並沒有運行最新版本的 Android 系統。
  說到性能,Kotlin 也沒有帶來任何負面影響。Kotlin 編譯器生成的代碼執行起來和普通的 Java 代碼效率一樣。Kotlin 使用的運行時(庫)體積相當小,所以編譯出來的應用程序包體積也不會增加多少。當你使用 lambda 的時候,它們會被許多 Kotlin 標準庫函數內聯。lambda 的內聯確保不會創建新對象,因此應用程序也不必忍受額外的 GC 暫停。

Kotlin初體驗