最近因為公司的前輩很推 Kotlin,想說就來玩一下看看,畢竟那個前輩推薦的東西通常不會錯(之前也是因為他推 TypeScript 才來玩,而一玩就回不去了)。要學 Kotlin,最快的方法就是拿一個現成的小規模 Java App,叫 Android Studio 用內建功能自動轉換成 Kotlin 程式碼,然後看看轉出來長什麼樣子邊看邊學。我就拿了我自己寫的小帳本程式來轉換看看;該程式的主體是用網頁寫的,所以原生的部份只是一個用來包網頁的 WebView 以及一些會在背景發送 API 的簡單程式碼,非常適合這次的實驗。

就結果來說,很明顯地,Android Studio 內建的 Kotlin 轉換功能還很有待改進,即便像我這種很簡單的小程式,它也沒辦法一次轉出可以順利編譯的程式碼,而有若干地方需要我修正。幾乎所有的那種修正都跟 Kotlin 本身有較強的 null 型別檢查這件事情有關:在 Java 當中,任何參考型別的變數之值都有可能會是 null,這個特性常常會導致我們需要做很多麻煩的 null 檢查以避免程式在執行期出錯,而 Kotlin 為了解決這個問題,預設情況中參考型別都是不可 null 的,必須加上「?」(例如 String?)才會是可 null 型別,於是從 Java 直接轉換成 Kotlin 的程式當中大部分的參考型別宣告都會基於相容性的緣故變成了 ? 型別,而大部分的編譯錯誤都是因此跑出來的。

例如,我常常會在 Activity 裡面宣告一些私有變數來存放一些 View 的參考,以便可以跨方法使用。這些變數不可能一開始就初始化,因為它需要用 context 的 findViewById(...) 方法取出。可是如果宣告變數的型態為可 null,那就需要到處加上煩人的 !! 來註明這個變數其實真的不是 null,或是到處用 ?. 來防範。幸好,Kotlin 有兩種方式可以解決這個問題。如果是 var 宣告的變數,可以搭配 lateinit 關鍵字來告訴編譯器「我待會會初始化,相信我就是了」;而如果是 val 宣告的常數,可以搭配 by lazy {...} 來讓常數在第一次被呼叫的時候自動初始化。我尤其喜歡後者的作法,因為它非常安全,絕對不會因為程式順序寫錯了就導致不小心沒有初始化到。

另外在一些泛型類別或方法的宣告上,直接 Java 轉 Kotlin 也很容易冒出編譯錯誤,原因同樣是因為轉換器常常自動把型別加上 ?,可是卻在類別或方法內部的使用上搭不起來。修正的方法視情境而定有很多不同的,有的時候要讓泛型參數繼承 Any 型別,有的時候可以改用 data class 宣告以簡化物件的初始化寫法(這是 Kotlin 其中一個很神的特性,必玩),這些 Kotlin 的特性弄懂了之後就見招拆招就是了;因為不同情境試用的解法也不同,也怪不得自動轉換程式難以替我們自行做這些決策。

而修正了這些跟 null 有關的問題之後,程式很快就編譯成功了。轉換成 Kotlin 之後程式碼確實在一些地方有變得比較簡潔一些。一個很值得一提的特性是,Kotlin 不但自身有 getter setter 的語法,當 Kotlin 看到一個傳統的 Java 類別上面同時定義有 getXXXsetXXX 方法而且型別符合的時候,Kotlin 也可以讓我們直接用 object.XXX 的屬性寫法來存取它,這個設計真的頗有意思的。

在這個官方網頁上有整理了很多 Kotlin 比 Java 要多出的語言特性,很多都是在 C# 裡頭用得很開心的東西,現在 Kotlin 也都有了,實在是件不錯的事。

分享此頁至:
最後修改日期: 2020/06/24

留言

撰寫回覆或留言

發佈留言必須填寫的電子郵件地址不會公開。