初見Kotlin

2gua發表於2016-03-17

enter image description here

原文連結在我的部落格: 初見Kotlin

前段日子Kotlin釋出了1.0版,號稱Android界的Swift,一副拯救苦逼Android碼農於水火中的架勢。那麼它有何神奇之處呢?經過一段時間的摸索,Kotlin在我眼中定位為“通吃型”語言,服務端不是問題,還能生成JavaScript,另外又有針對Android開發的增強服務特性。

enter image description here

Kotlin一出世就被拿來跟Scala比較,恰巧Scala也是我喜歡的一門JVM語言。其實,我個人感覺Kotlin並非Scala的小弟,只是說有了valvar的表象,給許多開發者這種感覺,但要這麼說的話,Scala的函式/方法定義用def而Kotlin卻用fun這又怎麼說呢?此外,Kotlin的伴生物件跟Scala中的概念也區別不小。另一種說法是Kotlin比Scala簡單,也許Kotlin尚未實現Scala中我喜歡的yield功能,但要從發展角度來看,Kotlin出道時間短,後續要新增的功能應該會層出不窮。Kotlin的用法並不簡單要說“骨子裡”的像,我寧願覺得Kotlin更像Java

  • 號稱“100% interoperable with Java™”:使用一段時間的感受是,寫著寫著,就有可能不知不覺中無縫地使用Java程式碼風格,寫完了才恍然大悟......
  • 一方面Kotlin新增了大量函式式風格,它的map、filter等等操作比Java 8 Stream的來得簡潔直接。但隱約感受得到,這只是Kotlin特性中表面且很小的一部分,涉及過多的功能需求時,還是要尋求Java的特性支援。這點不像Scala,Scala是能不用Java原生特性就不用,而Kotlin則是需要用時你用就是了。不知道這是好事還是壞事,但終究看個人偏好吧,對於Java底子較好或較適應Java風格的開發者來說,這則是一個不錯的起點。

上面是我覺得有必要澄清的一些概念。而以下則是Kotlin吸引我研究下去的一些因素

  • Null Safety:別小看這一點,強迫症真能防範大量潛在問題。
  • Extension Functions:很喜歡這個特性,即開即用的感覺。
  • JetBrains家的東西,還是有些功底的,更重要的是跟聞名遐邇的自家開發工具必然結合緊密。
  • 充分考慮Android平臺開發的環境及特性適配,也難怪稱為“Android開發的Swift”。
  • 與Java的互操作性:也許會讓大票開發者不感冒,但對我來說再好不過。

以下是Kotlin的一些限制,當然也只是我個人的一些看法:

  • 穩定性:尚待提高,特別是REPL工具,動不動就導致退出REPL環境。此外,程式碼中的空行及註釋都會在REPL中引發異常。
  • 文件:官方文件比較初級,且資源不多。
  • 普及率:小眾。TIOBE從上月起已經納入了Kotlin的觀察,首月排名貌似158位。

整個感受,用這個示例實現來解釋最恰當不過了:

給一個自定義日期類新增ComparisonRangeFor迭代功能。

data class MyDate(val year: Int, val month: Int, val dayOfMonth: Int) : Comparable<MyDate> {
    override fun compareTo(other: MyDate): Int {
        val calendar1: java.util.Calendar = java.util.Calendar.getInstance()
        val calendar2: java.util.Calendar = java.util.Calendar.getInstance()
        calendar1.set(this.year, this.month - 1, this.dayOfMonth)
        calendar2.set(other.year, other.month - 1, other.dayOfMonth)

        return if (calendar1.before(calendar2)) -1 else if (calendar1.after(calendar2)) 1 else 0
    }
}

class DateRange(val start: MyDate, val end: MyDate): Iterable<MyDate> {
    private var _dateRange: MutableList<MyDate> = mutableListOf<MyDate>()

    init {
        val calendar1: java.util.Calendar = java.util.Calendar.getInstance()
        val calendar2: java.util.Calendar = java.util.Calendar.getInstance()
        calendar1.set(start.year, start.month - 1, start.dayOfMonth)
        calendar2.set(end.year, end.month - 1, end.dayOfMonth)

        if (start < end) {
            _dateRange.add(MyDate(calendar1.get(java.util.Calendar.YEAR),
                                  calendar1.get(java.util.Calendar.MONTH) + 1,
                                  calendar1.get(java.util.Calendar.DAY_OF_MONTH)))

            val days = (calendar2.getTimeInMillis() - calendar1.getTimeInMillis()) / 1000 / 60 /60 /24
            for (day in 1..days) {
                calendar1.add(java.util.Calendar.DAY_OF_MONTH, 1)
                _dateRange.add(MyDate(calendar1.get(java.util.Calendar.YEAR),
                                      calendar1.get(java.util.Calendar.MONTH) + 1,
                                      calendar1.get(java.util.Calendar.DAY_OF_MONTH)))
            }
        }
    }

    override operator fun iterator(): Iterator<MyDate> {
        return MyDateIterator()
    }

    inner class MyDateIterator: Iterator<MyDate> {
        private var _index: Int = 0

        override operator fun hasNext(): Boolean {
            return _index != _dateRange.size
        }

        override operator fun next(): MyDate {
            return _dateRange[_index++];
        }
    }
}

operator fun MyDate.rangeTo(other: MyDate): DateRange {
    return DateRange(this, other)
}

測試程式碼:

>>> fun iterateOverDateRange(firstDate: MyDate, secondDate: MyDate, handler: (MyDate) -> Unit) {
...     for (date in firstDate..secondDate) {
...         handler(date)
...     }
... }
>>> iterateOverDateRange(MyDate(2016, 3, 10), MyDate(2016, 3, 14)) { println(it) }
MyDate(year=2016, month=3, dayOfMonth=10)
MyDate(year=2016, month=3, dayOfMonth=11)
MyDate(year=2016, month=3, dayOfMonth=12)
MyDate(year=2016, month=3, dayOfMonth=13)
MyDate(year=2016, month=3, dayOfMonth=14)
>>> iterateOverDateRange(MyDate(2016, 5, 1), MyDate(2016, 5, 5)) { println(it) }
MyDate(year=2016, month=5, dayOfMonth=1)
MyDate(year=2016, month=5, dayOfMonth=2)
MyDate(year=2016, month=5, dayOfMonth=3)
MyDate(year=2016, month=5, dayOfMonth=4)
MyDate(year=2016, month=5, dayOfMonth=5)
>>> iterateOverDateRange(MyDate(2016, 5, 1), MyDate(2016, 5, 1)) { println(it) }
>>> (MyDate(2016, 3, 10)..MyDate(2016, 3, 14)).map { println("${it.year}-${it.month}-${it.dayOfMonth}") }
2016-3-10
2016-3-11
2016-3-12
2016-3-13
2016-3-14
[kotlin.Unit, kotlin.Unit, kotlin.Unit, kotlin.Unit, kotlin.Unit]
>>>
>>> fun checkInRange(date: MyDate, first: MyDate, last: MyDate): Boolean {
...     return date in first..last
... }
>>> checkInRange(MyDate(2016, 3, 13), MyDate(2016, 3, 10), MyDate(2016, 3, 14))
true
>>> checkInRange(MyDate(2016, 3, 8), MyDate(2016, 3, 10), MyDate(2016, 3, 14))
false
>>> checkInRange(MyDate(2015, 3, 13), MyDate(2016, 3, 10), MyDate(2016, 3, 14))
false
>>>

結論:

剛好現在看到一個開發者的困惑:“Kotlin讓我感到不安。它離開Java 能玩得起嗎 ?”我覺得Kotlin發展的前提是不離開Java,這個前提沒了,估計Scala、Clojure一樣沒戲——Kotlin本身就是Java形態的增強與補充!

該不該學習Kotlin?——Kotlin就是一門語言,不是什麼“十全大補丸”,但其現在面臨的最急迫任務就是普及與推廣,至少要達到Groovy、Scala或Clojure等“同門師兄弟”的地位,雖然這些個JVM語言的排名也不是很高。“該不該學”這個問題真沒有答案。總之,它就是一門新興的語言而已。

enter image description here

相關文章