Kotlin Type? vs Scala Option

本文由 KnewHow 發表在 ScalaCool 團隊博客。編程

最近閱讀一些關於 Kotlin 類型系統方面的書,發現 Kotlin 的類型系統針對 null 有着獨特的設計哲學。在 Java 或者其它編程語言中,常常會出現 NullPointerException,而致使此異常的重要緣由是由於你能夠寫 String s = null 這樣的代碼。其實能夠認爲這是 Java 等語言類型系統設計的一個缺陷,它們容許 null 能夠做爲任何類型的值!安全

可是在 Kotlin 中,若是你聲明 val s: String = null,那麼編譯器會給你一個 error,由於在 Kotlin 中,你不容許把一個 null 值賦給一個普通的類型。若是你聲明一個這樣的函數 fun strLen(s: String) = {...},那麼這個函數將不接受值爲 null 的參數。編程語言

這個設計看起來如此的美好,他能夠極大程度的減小 Kotlin 產生 NullPointerException,但是若是有一天,你須要調用一個方法,它的返回值可能爲 null 也可能爲 String ,那麼在 Kotlin 中你能夠聲明一個可空的字符串類型:String?val s: String? = null 此時 Kotlin 的編譯器會讓這行代碼經過。固然它也能夠接收一個普通的 String 類型的值 val s: String? = "abc"函數

可空類型(Type?)的設計,是 Kotlin 另外一個設計哲學,它要求工程師在設計的時候就須要肯定該變量是否可爲空。若是不爲空就使用Type 類型聲明,不然就使用 Type? 類型聲明。這讓我想起在 Scala 中存在一個和 Type? 有着殊途同歸之妙的一個類型—— Option[T]性能

Option[T] 有兩個子類型:Some[T]None,你可使用 val s: Option[String] = Some("123") 來表示一個字符串存在,固然你可使用val s: Option[String] = None 來表示這個字符串不存在。測試

Scala 和 Kotlin 都是基於 JVM 的編程語言,而 Option[T]Type? 的設計就是用來解決 JVM 平臺出現的 NullPointerException。但兩者的設計理念卻大相徑庭,Scala 的 Option[T] 是在原有類型基礎上使用 Option 作一層封裝,而 Koltin 的 Type? 是使用語法糖完成的。ui

那麼這兩種設計方案到底誰更好一點呢?咱們將會使用如下標準來分別測試它們:spa

  • 是否能夠完美的規避 NullPointerException —— 兩者的設計都是爲了解決 NullPointerException,誰能夠更好的規避這個問題呢?
  • 代碼的可讀性 —— 若是在複雜的業務中,誰的代碼可讀性更好一點呢?
  • 性能

規避空指針

在上文中,咱們曾經提過,NullPointerException 產生的緣由是你能夠把一個 null 的值傳遞給一個類型的變量,而後調用這個類型的方法。咱們可使用 Java 的代碼來表示一下:String s = null; s.length()scala

Type? 的設計理念中,對於不肯定是否爲 null 類型可使用 Type? 類型來聲明,如val s: String? = getString...,此時 s 的類型是 String?,你不能直接調用 s.length,你須要進行安全調用s?.length。這個函數的返回類型是一個 Int?,這很正常,對於一個不肯定是否爲 null 的類型進行安全調用返回固然是一個 Type? 類型。若是 s 不爲 null 正常返回 s 的長度,不然返回 null。除此以外, Kotlin 還針對 Type? 提供了 Elvis 操做和 let 函數,具體的用法能夠參考 Kotlin 官方手冊。設計

而在 Optional 的設計哲學中,你可使用 Option[T] 來包裹一個不肯定是否爲 null 的值。這裏咱們使用 Scala 的代碼來演示:val s: Option[String] = Option(getString...),此時 s 的類型爲 Option[String],你仍然不能直接調用s.length,你可使用 map 函數:s.map(s => s.length),它的返回值是一個 Option[Int] 類型。和 Type? 很相似,對一個 Option[T] 類型使用 map 函數,結果固然是一個 Option[S] 類型。在 Scala 中,你也可使用模式匹配來處理 Option 類型。

總結:兩者均可以完美的規避 NullPointerExceptionType? 使用安全調用來避免直接調用 Type 類型的方法,而 Option 則使用 map 函數或者模式匹配來處理。本質上都是避免直接調用值可能爲 null 的類型變量的方法。

代碼可讀性

實際的業務是比較複雜的,例如,咱們須要計算兩個數字字符串的乘積,首先咱們須要把他們轉換爲 Int 類型,若是其中一個字符串是轉換失敗,則沒法計算結果。

在 Kotlin 的 Type? 中,咱們須要從新定義 String 類型的 toInt 方法,讓它返回一個 Int? 類型,代碼以下:

fun tryString2Int(a: String) = try {
    a.toInt()
}catch (e:Exception){
    null
}
複製代碼

而後咱們須要定義一個方法來計算兩個數字字符串的乘積,這裏咱們使用 Type? 的 let 函數,它接受一個 Lambda 表達式,若是調用者的值不爲 null,則調用 Lambda 表達式,不然直接返回 nullstrNumberMuti 函數返回的是一個 Double? 類型,若是有任何一個字符串轉換數字失敗,就返回 null,都轉換成功才計算乘積。

fun strNumberMuti(s1: String, s2: String): Double? =
    tryString2Int(s1)?.let{ a ->
        tryString2Int(s2)?.let {
            t -> a * t * 1.0 }}
複製代碼

這段代碼的可讀取有點差呀,並且在實際的業務開發過程當中,可能會有更多的 Type? 類型,那代碼豈不是要爆炸了!。幸運的是,Kotlin 容許咱們使用 if 來代替 let 函數 作相同的判斷,代碼以下:

fun strNumberMuti2(s1: String, s2: String):Double? {
    val a = tryString2Int(s1)
    val b = tryString2Int(s2)
    return if(a!=null && b!= null) a * b * 1.0 else null
}
複製代碼

這樣的代碼可讀性就好多了,可是丟失函數式的編程美感。並且感受 Type? 是一種語法糖,手動對 Type? 進行非空校驗,就能夠直接使用 Type 類型了!!

一樣的咱們使用 Scala 的 Option[T] 來完成上面的需求,爲了讓 toInt 函數返回 Option[T] 類型,咱們定義了一個 Try 函數,這個函數看不懂不要緊,你只需知道它接受一個函數,而且返回一個 Option[A] 值便可。代碼讓以下:

def Try[A](a: => A): Option[A] = {
    try Some(a)
    catch {case e: Exception => None}
  }
複製代碼

一樣的,咱們須要寫一個函數,用來把兩個字符串數字轉換爲整數,而且作它們的乘積,這裏咱們爲了使代碼更簡潔,使用了 Scala 的 for 推導,具體的用法能夠參考 Scala 官方的 Document。strNumberNu返回類型是 Option[Double],若是有任何一個轉換失敗,返回 None,不然返回 Some[Double],代碼以下:

def strNumberMuti(s1: String, s2: String): Option[Double] = {
    for{
      a <- Try{ s1.toInt }
      b <- Try{ s2.toInt }
    } yield a * b

  }
複製代碼

能夠看出,使用 Scala 的 Option[T] 更具備函數式的編程美感,並且代碼的可讀性極強,並且即便有更多的 Option[T],for 推導均可以輕鬆應對。

總結:面對比較複雜的業務場景,Type?Option[T] 均可以輕鬆應對,可是 Type? 的用法就顯得有些 low,仍是使用 !=null 的套路,這也暴露了它的設計是存在缺陷的。相反的 Option[T] 的設計理念是完備的,並且極具函數式的編程美感。

性能

性能是衡量設計好壞的一個重要的方面,下面咱們只作一個簡單的測試:讓兩個字符串都是"999",而後分別執行 Kotlin 的 strNumberMuti 和 Scala 的 strNumberMuti 一千萬次,而後咱們發現 Kotlin 的 strNumberMuti 執行時間大約在 1.9s,而 Scala 的 strNumberMuti 執行時間約在 5.0s。由此能夠看出,Kotlin 的 Type? 比 Scala Option[T] 擁有更好的性能,其實這樣很正常,由於 Kotlin 的 Type? 是語法糖,建立一個 Type? 的對象其實和建立一個 Type 的對象其實消耗的性能差很少,可是 Option[T]不只僅須要建立 T 類型的對象,更須要建立 Option[T] 類型的對象來包裹 T 類型的對象,所以它的開銷大一點。

總結

就我而言,我更喜歡 Scala 的 Option[T] 的設計,由於它是理論完備的,並且極具函數式的編程美感,即便它的性能要差一點。對於 Kotlin 的 Type? 類型,我以爲它的設計有瑕疵,就拿 let 函數舉例,在單個 Type? 很好用,可是當多個 Type? 進行組合的時候,就顯得很雞肋。

蘿蔔青菜,各有所愛,也許某天 Kotlin 也會讓 Type? 具備函數式的編程美感。

相關文章
相關標籤/搜索