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? 具备函数式的编程美感。

相关文章
相关标签/搜索