Kotlin Night Taipei 2018 參與心得

LINE的公司文化希望員工能積極在內部分享新技術或架構的研究心得,也很鼓勵員工參與外部社群的分享與活動。像是去年(2018)也負擔了公司內幾位 iOS 工程師的門票費用,讓同事能去參與台灣 iOS 開發者的盛會 iPlayground
一直很喜歡公司這種推廣與分享的態度,期許自己也能多去做技術分享參與這類活動。碰巧去年底很榮幸收到 Steve Tian 及 WeTogether 的 Jane Shih 邀請,讓我有機會與LINE京都的同事 Freddie 大大一起參加 Kotlin Night 並分享 Kotlin 開發的相關經驗。
因為 Kotlin 是一個新興的開發語言,社群人數比起其他深具歷史的語言自然尚未那麼成熟與龐大,但是 JetBrains 很積極的在推廣並贊助世界各地的社群活動,Kotlin Nights 就是其中之一。接下來,我將針對所參與的台北 2018 一年一度 Kotlin Night 的活動做介紹。

什麼是 Kotlin Night?

Kotlin Night 是一次性活動,通常包括 Kotlin 3 到 4 個 talk。Talk 必須專注在對 Kotlin 使用或是其他一些兼容 Kotlin 的技術,而不是營銷,銷售或產品推廣。JetBrains 樂於支持這類的社群活動,只要你遵循其設定的規範,並於活動前提出申請,就能收到他們資助的T恤、貼紙當做紀念品。底下附上官網相關規範供各位參考:Kotlin Nights Guidelines

Kotlin Night Taipei 2018

這次台北的 Kotlin Night 由 WeTogether ,活動時間是11月30日週五晚上7點,地點辦在三創11F,會場很溫馨小巧,大約可容納 40-50 人的場地,當日提供簡單餐食並且有T恤和貼紙作為參加者的紀念品。
活動訊息:Kotlin Night Taipei 2018

Syntrend 三創生活園區

會場佈置

講者視角

現場提供簡單餐食

紀念品 – 貼紙

報名收費與紀念品索取處

活動議程介紹

這次的 meetup 主要目的是希望推廣 Kotlin 這個語言,所以預期參與的大部分開發者對 Kotlin 處於初了解或觀望的階段,3 位講者皆以 Kotlin 能帶來的好處為出發點來介紹。當日也有稍微調查了一下,參與者 30 幾位開發者中,有將近 2/3 的人是 App Developer,剩下的則分別為 Backend 以 Frontend Developer,這人數大約也可見識到 Google 在 2017 Google IO 上宣布將 Kotlin 納為 Android 官方開發語言對 App Developer 的影響。

下面我會簡略提到其他兩位講者的分享內容,並針對當天我的投影片進一步做解說。

結束後的大合照

過程合照

My Journey with Kotlin By Steve

開場由一位熱心的 Backend Developer Steve Tian 做介紹,他分享了如何開始使用 Kotlin 的歷程,也提到了在荷蘭參與 2018 KotlinConf 的精采過程,最後甚至還 live demo 了從 Java 轉到 Kotlin 能如何精簡程式碼的技巧。印象很深刻的一點是,在分享過程中,他不斷流露出對於他參與 KotlinConf 時身旁坐著一群由公司資助去參加的日本 LINE 工程師的欣羨之情,也很讚賞 LINE 對於技術研討會支持的態度。

Steve Live Demo

Kotlin vs. Java for Android Development By Rene

這個主題由我分享,坦白說在分享當時,真正把 Kotlin 用在工作的 Project 還未滿一年,經驗尚不是很豐富,但依然很開心能把實際應用上遇到的優缺點跟大家分享,藉此也希望推廣 Kotlin 這個新起之秀,讓大家見識到它的吸引力,進一步讓大家體驗跟我一樣「回不去」Java 的心情。由於這部分是我準備的,所以在本篇介紹中也會對我的這部分著墨較多。

先附上當日 Slides:Kotlin vs Java For Android

這個介紹主要分為三個部分:

1. Kotlin的特性

Kotlin的特性相當多,這邊主要挑出幾個 Android Developer 會特別關注的重點講述。

  • Interoperability (相互操作性)
    Kotlin 可與 Java 可100%相互操作。即使想改用 Kotlin 開發也完全無需放棄舊的 Java code,且仍可使用既有的 Java/Android 3rd-party library。
  • Null-Safety (空安全)
    Kotlin 的設計理念之一就是希望消除程式碼中 null reference 造成 NullPointerException 的危害。因此,Kotlin 在變數宣告時便已決定變數能否為空值,對於可空變數,在存取或操作時必須做相對應的空值處理,否則編譯器將直接報錯無法編譯成功。如此即可大大減少程式碼中繁瑣的空值判斷, 並盡可能避免 NullPointerException 的產生。
  • Data Classes (資料類別)
    App 通常需要一些基本的類別來封裝 Domain 相關物件,而 Kotlin 的 data class 減少了 Java 中不可避免的樣板的方法,宣告成 data class 的物件,在編譯時 compiler 將自動產生 getter, setter, equals(), hashCode(), toString() 以及 copy() 等方法,大幅精簡程式碼。
  • Extension Functions (擴展函數)
    JetBrains 在設計動機中提到,我們很習慣 Java 中的一些以 Utils 結尾的工具類別,像是 FileUtils, StringUtils 等等。使用這些工具類的程式碼容易顯得冗長,且可讀性較差。Kotlin 中則採用 extension 的概念來大幅度增加可讀性並降低程式碼量。
  • Lean Syntax and Concise(精準簡潔的語法)
    Kotlin 有著比 Java8 更精簡的 lambda expression,其他語法設計也皆體現出簡潔的語句與更好的維護性。這篇文章 Lessons from converting an app to 100% Kotlin – Keepsafe Engineering 中也分享了將一個原有的 App 從 Java 改寫為 Kotlin 的經驗,結果顯示程式碼少了30%,其中函數量也少了10%,這也在在證明了 Kotlin 相較於 Java 來說是更簡潔的語言。

2. 應用在 Android 上的優缺點

首先,針對一些 Kotlin 開發上的優點,對於寫 Java 的開發者列舉了一些範例如下:

  • 跟 findViewById 說再見:Kotlin 提供了 kotlin-android-extensions 方便 Android 開發上的許多操作,像是可直接對 view id 做操作,而不用再冗贅的先從根節點開始找 view ,之後才能對其做操作。
    Java Kotlin
    // native android
    TextView textView = (TextView) findViewById(R.id.textView);
    textView.setText("Hello World");
     
     
    // ButterKnife
    @BindView(R.id.textView) TextView textView;
    @OnClick(R.id.buttonRetry) void onRetry(Button button);
    
    textView.text = "Hello World"
    
  • 精簡的 data class 及 Parcelize:
    在 Java 的開發環境中,你需要為每一個資料欄位補上 setter, getter 及其他需要的函式,且在 Android 中如果需要在 activity 或 fragment 中傳遞資料,你還必須實作 Parcelable,則需要覆寫的函數就更多了。在 Kotlin 中一樣提供 Android 開發者簡潔有力的資料類別,直接看範例就能體會到精簡多少程式碼了!

    Java Kotlin
    public class Person implements Parcelable {
     
        private int age;
        private String name;
     
        protected Person(Parcel in) {
            age = in.readInt();
            name = in.readString();
        }
     
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(age);
            dest.writeString(name);
        }
     
        @Override
        public int describeContents() {
            return 0;
        }
     
        public static final Creator CREATOR = new Creator() {
            @Override
            public Person createFromParcel(Parcel in) {
                return new Person(in);
            }
     
            @Override
            public Person[] newArray(int size) {
                return new Person[size];
            }
        };
    }
    
    @Parcelize
    data class Person(
        val age: Int,
        val name: String
    ) : Parcelable
    
  • 初始化物件:
    Kotlin 提供了需多便利的擴展函數,像是 apply, let, with, run, also 等等。這邊針對 Android 開發,我想特別提到關於物件初始化時 Kotlin 相對於 Java 更直覺且簡潔的語法,身為工程師一樣看程式碼解釋一切:

    Java Kotlin
    TextView textViewDescription = new TextView(this);
    textViewDescription.setText(Html.fromHtml(article.content));
    textViewDescription.setEnabled(article.isSupported());
    textViewDescription.setMovementMethod(LinkMovementMethod.getInstance());
    
    val textViewDescription = TextView(this).apply {
        text = article.content.toSpanned()
        isEnabled = article.isSupported()
        movementMethod = LinkMovementMethod.getInstance()
    }
    
  • Default And Named Arguments:
    Kotlin 的函數參數可以擁有預設值以及指定參數名稱,當省略傳入相應的參數值時將使用預設值。而這相較於其他語言,將大大減少 overloading function 的數量。

    Kotlin
    // api interface
    fun getComments(
        id: String,
        commentSize: Int = 15,
        parentCommentSn: String? = null,
        ccCookie: String? = null,
        sort: SortCategory = SortCategory.POPULAR,
        direction: Direction = Direction.DESC
    ): Single
     
    // usage
    apiClient.getComments(id = articleId)
     
    apiClient.getComments(id = articleId, sort = LATEST, ccCookie = ccCookie)
    
  • Higher-Order Functions And Lambda:
    高階函數是一種特殊的函數,這種函數接受函數作為參數,也能返回函數當作結果。這邊以 Android 常見的 UI component作為範例,在實作recyclerview 時總是要傳入 onClickListener 給 Adapter 來操作點擊後的動作。這時候如果直接把 onClick function 搭配 lambda 表示式來當作參數傳入,程式碼便又能精簡很多。

    Kotlin
    // pass function as an argument
    class SearchResultAdapter(
        private val onArticleClickAction: (Int, SearchedArticle) -> Unit
    ) : RecyclerView.Adapter() {
          override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder =
            ArticleViewHolder(inflatedView).listen { position, article ->
                onArticleClickAction(position, view.tag as SearchedArticle)
            }
    }
     
    // lambda usage
    private val resultAdapter: SearchResultAdapter =
        SearchResultAdapter { _, article ->
            startActivity(ArticleActivity.createIntent(context, article.url))
        }
    
  • 擴展函數:
    這大概是 Kotlin 中最方便的功能了。擴展函數允許開發人員在非自己開發的類別或是在不改變已存在類別中,加入新的函數。

    Kotlin
    // add listen extension to RecyclerView.ViewHolder
    fun  T.listen(action: (view: View, position: Int) -> Unit): T {
        itemView.setOnClickListener { action(it, adapterPosition) }
        return this
    }
    // implementation
    class SearchResultAdapter(
        private val onArticleClickAction: (Int, SearchedArticle) -> Unit
    ) : RecyclerView.Adapter() {
          override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder =
            ArticleViewHolder(inflatedView).listen { position, article ->
                onArticleClickAction(position, view.tag as SearchedArticle)
            }
    }
    
    Kotlin
    // add animation extension to View object
    fun View.slideExit() {
        if (translationY == 0f) animate().translationY(-height.toFloat())
    }
     
    textViewTitle.slideExit()
     
    // ease visibility setting of View
    fun View.visibleElseGone(visible: Boolean) {
        visibility = if (visible) View.VISIBLE else View.GONE
    }
     
    recyclerViewHistory.visibleElseGone(isVisible)
     
    // add toDateString extension to Long object
    fun Long.toDateString(dateFormat: Int = DateFormat.MEDIUM): String =
        DateFormat.getDateInstance(dateFormat, Locale.getDefault()).let {
            it.format(this)
        }
     
    System.currentTimeMillis().toDateString()
    
    Kotlin
    // add onTextChange listener extension to EditText
    inline fun EditText.onTextChange(crossinline f: (CharSequence, Int, Int, Int) -> Unit) {
        val listener = object : SimpleTextWatcher() {
            override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) =
                f(s, start, before, count)
        }
        this.addTextChangedListener(listener)
    }
     
    // simplify TextWatcher to prevent unnecessary overriding
    abstract class SimpleTextWatcher : TextWatcher {
        override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
     
        override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
     
        override fun afterTextChanged(s: Editable) {}
    }
     
    // final simplified use of onTextChange
    editText.onTextChange { str, _, _, _ ->
        filter(str)
    }
    
  • 目前已知的一些小缺點:
    • 因為加入了 Kotlin 函式庫,所以在建構 Android apk 檔案時會增加 800 KB – 1 MB.Fewer resources
    • 根據實驗,對於沒有使用 Gradle daemon,Clean build 的編譯速度會比 Java 慢 1x%。但對於 incremental build 來說,Kotlin 的編譯速率會等於或略快於 Java。
    • 如前面提到,因為 Kotlin 是新興語言,資源或是討論度可能還無法跟 Java 相比,但近年有逐年翻增的趨勢。
  • 小結論:
    • 雖然 Kotlin 仍然有一些小缺陷,但總體來說依然是很正面的評價。對於寫 Java 的開發者而言,學習 Kotlin 的曲線平緩,況且大部分的專案都不是一次性的,因此,精簡的語法、更直覺的語句還有更好的維護性都很值得花一點成本來作轉換。Kotlin 絕對不會令你後悔!

3. 由 Pusher 公司市調 Kotlin 的使用現況

出處: State of Kotlin 2018

最後這個部分,我相信看完了許多 Kotlin 的特性後,應該也會跟我一樣有這些疑惑:「到底有多少開發人員也在用?」、「哪些功能是真的有幫助的?」、「有很多公司有真的在用了嗎?」…等等。針對這些問題我也上網搜尋試圖找到答案,剛好 Pusher 這間公司在 2018 年初針對 2,744 位人員花了三個月時間做調查,底下針對他的調查結果我也截錄了一些重點,如果有興趣可以到上面提供的網站做更深入的了解。

  • 01. 較年輕的開發人員會喜歡 Kotlin。
  • 02. Kotlin 無疑地正在邁向成功:尤其是 2017 五月之後,在 Google 宣布 Kotlin 列為 Android 開發的官方語言之一後,更是爆炸性的成長。
  • 03. JetBrains 的付出正在得到回報:報告中顯示,有超過 60% 的人在工作專案中採用 Kotlin,其中在 Android 中採用的更是。
  • 04. 不同特性皆有人愛:Null Safety 毫無無疑是最受到開發人員歡迎的特性,其他包含擴展函數、可與 Java 100%相互操作以及資料類別都有超過50%的支持度。

To Kotlin, or not to Kotlin, That is the Question By Freddie

Freddie 大大在這場裡,很精彩的針對 Kotlin 各種特性做更深入的講解,並附上相關程式碼供大家參考。非常推薦大家有空能看看他準備的投影片!

Slides:To Kotlin, or not to Kotlin, That is the Question

總結

整體來說,真的很榮幸能參與了這個活動!身為一個工程師總是會想追求最新的技術,而這一個分享恰好也讓我有機會能再次檢視轉換成 Koltin 後所帶來的優缺點,並能提供給想進一步接觸的人一點概念與幫助。在這樣的會議中,總是能認識新的朋友,也經常在問題討論中找到其他不同面向的觀點,既能使自身成長也能帶點新的想法回到公司。在 LINE 內部的 tech sharing 是從不間斷的,而這種外部活動更能刺激思考,很推薦開發人員能積極參與類似的社群活動,讓自身永遠保持進步,不在技術洪流中被滅頂!