空安全和平台类型

我们知道Java 中的任何引用都可能是null,这样我们在使用 Kotlin 调用来自 Java 的对象的时候就有可能会出现空安全的问题。

Java 声明的类型在 Kotlin 中会被特别对待并称为平台类型(platform types )。对这种类型的空检查会放宽,因此它们的安全保证与在 Java 中相同。

请看以下示例:

  1. @RunWith(JUnit4::class)
  2. class CallingJavaNullSafe {
  3. @Test fun testCallingJavaNullSafe() {
  4. val product = Product()
  5. // product.name = null
  6. product.category = "金融财务类"
  7. product.gmtCreated = Date()
  8. product.gmtModified = Date()
  9. println(JSONUtils.toJsonString(product))
  10. val name = product.name
  11. println("product name is ${name}")
  12. val eqName = name == "账务系统"
  13. println(eqName)
  14. name.substring(1)
  15. }
  16. }

上面的代码可以正确编译通过。Kotlin编译器对来自Java的空值name(平台类型)放宽了空检查name.substring(1)。但是这样的空指针异常仍然会在运行时抛出来。

运行上面的代码,我们可以看到输出:

  1. {"category":"金融财务类","gmtCreated":1500050426817,"gmtModified":1500050426817}
  2. product name is null
  3. false
  4. null cannot be cast to non-null type java.lang.String
  5. kotlin.TypeCastException: null cannot be cast to non-null type java.lang.String
  6. at com.easy.kotlin.CallingJavaNullSafe.testCallingJavaNullSafe(CallingJavaNullSafe.kt:27)

我们没有设置name的值,在Java它就是null。我们在Kotlin代码中使用了这个name进行计算,我们可以看出:

  1. val eqName = name == "账务系统"
  2. println(eqName)

可以正确输出false。这表明Kotlin的判断字符串是否相等已经对null的情况作了判断处理,这样的代码如果在Java中调用 name.equals("账务系统") 就该抛空指针异常了。

但是当我们直接使用name这个值来调用name.substring(1)的时候,Kotlin编译器不会检查这个空异常,但是运行时还是要报错的:null cannot be cast to non-null type java.lang.String

如果我们不想看到这样的异常,而是当name是null的时候,安静的输出null,直接使用Kotlin中的空安全的调用 .?

  1. name?.substring(1)

这样,运行的时候不会抛出异常,直接安静的返回null。