欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Kotlin - 空安全

程序员文章站 2024-03-14 23:01:35
...

1.Kotlin空安全介绍

Kotlin空安全可消除来自代码空引用的危险。

2.什么是空引用?

  • 许多编程语言(包括 Java)中最常见的陷阱之一是访问空引用的成员,导致空引用异常。在 Java 中, 这等同于 NullPointerException 或简称 NPE。
  • NullPointerException概述
    • NPE是java.lang.NullPointerException的简称,是Java语言中的一个异常类,位于java.lang包中,父类是java.lang.RuntimeException,该异常在源程序中可以不进行捕获和处理。
    • 什么情况下会出现空引用异常?
      • 调用 null 对象的实例方法。
      • 访问或修改 null 对象的字段。
      • 如果一个数组为null,试图用属性length获得其长度时。
      • 如果一个数组为null,试图访问或修改其中某个元素时。
      • 在需要抛出一个异常对象,而该对象为 null 时。
      • 应用程序将会抛出NullPointerException类的实例,表明其他对 null 对象的非法使用。

3.Kotlin空安全的使用

在 Kotlin 中,定义变量时可以决定该变量是否可以接受null(空引用)

下面定义一个不接受空引用的变量

    var temp: String = "fish_leong"
    var length=temp.length//因为temp不接受空引用,所以它不会出现NPE,可以放心的各种. . .
    temp = null//会立刻被IDE检测出语法错误
    /***
     * Null can not be a value of a non-null type String,
     * 如果用Java写,IDE不会报错,编译也可以通过,由于代码没有进行空引用判断,最后执行必会抛出NullPointerException
     */
     println(temp.toString())

以上可以看出,写代码的时候难免会有疏漏,而Kotlin在IDE中对空引用进行了检查,避免了运行时出现NPE的情况。

下面定义一个可接受空引用的变量

  • 在定义变量类型时,在类型的后面加问号?即可,比如String?Any?
    var temp: String? = "fish_leong"//定义一个可接受空引用的变量
    val length = temp.length
    /**
     *  对上一行代码的解释
     *  这样IDE语法检查报错"Only safe(?.) or non-null assered (!!.)calls are allowed on a nullable receiver of type String?"
     *  因为temp不是空安全变量,所以不能像上面那样调用,要写成
     *  val length=temp!!.length //!! 操作符
     *  或
     *  val length=temp?.length//? 安全调用
     *  以上这两种写法的区别,在下面代码中会提到
     */
    println("temp的长度length$length")//此行输出"temp的长度length=10"

    // ?. 安全调用示例
    temp = null //temp可接受空引用
    val length2 = temp?.length
    /***
     * 对上一行代码的解释
     * 虽然temp是null,但这里用到了安全调用,
     * 此时temp?.length会返回一个null给length2
     */
    println("temp的长度length2=$length2")//会输出"temp的长度length2=null"

    println(temp?.toString())//此行输出 "null"

    println(temp.toString())//此行输出 "null"
    /**
     * 发现了没,上面这行代码,temp没有用空安全引用,也通过了IDE语法检查,这个在以后的文章再谈
     */
    println(temp!!.toString())//此行抛出"Exception in thread "main" kotlin.KotlinNullPointerException"异常,并终止,所以下面的代码是不能够被执行的

    println(temp.toString())//此行代码是将不会执行(在IDE中,也可以看到这行代码的颜色异常,它是Unreachable code(无法执行到的代码)),因为上一行代码使用了"!!"操作符,而temp又是null,所以抛出NPE终止

4.总结

  1. Kotlin定义变量时,通过给变量的类型后增加?操作符,可以设置该变量是否可以接受空引用(即可赋值为null)。
      var  a:String?=null//可接受空引用
      var  b:String="fish_leong"//不可接受空引用

2.不接受空引用的变量,在IDE中编写代码阶段,IDE通过语法检查帮助我们检查出NPE错误,对该类型的变量也不需要再做null判断,相对减少了不必要的代码,同时也减少了苦逼的debug、打log的排查错误的时间,提高了效率。

3.可接受空引用的变量的两种操作方式

  • 安全调用(Safe Calls)
    如果该变量时可接受空引用的变量,在调用它的成员属性或成员方法时,要使用?安全调用,这样即便变量是null,也不会抛出异常,只会返回null

    var strB:String?=null
    var numA = strB?.toInt()// numA=null
    
  • Elvis 操作符的使用
    如果 ?: 左侧表达式非空,elvis 操作符就返回其左侧表达式,否则返回右侧表达式。 请注意,当且仅当左侧是空(null)时,才会对右侧表达式求值。

    //例1
    var strB: String? =null
    var numA = strB?.toInt()// numA=null
    println(numA)//输出 null
    numA = if (strB != null) strB?.toInt() else 1//可写作   numA = strB?.toInt() ?: 1
    println(numA)//输出 1
    

    throw 、 return、break(官方文档没写break,但在循环中使用是可以的) 在 Kotlin 中都是表达式,所以它们也可以用在 elvis 操作符右侧。

    //例2
    val arrays = arrayListOf<String?>("1", "2", null, "3")
    for (element in arrays) {
        element ?: break
        println("case one:$element")
    }
    try {
        for (element in arrays) {
            element ?: throw Exception("NPE")
            println("case two:$element")
        }
    } catch (ex: Exception) {
        println("case two exception:$ex.message")
    }
    for (element in arrays) {
        element ?: return
        println("case three:$element")
    }
    //这里就不多解释了,看输出就懂了
    

    输出

    case one:1
    case one:2
    case two:1
    case two:2
    case two exception:NPE
    case three:1
    case three:2
    
  • !!操作符(The !! Operator)

    • 也没个飘准的英文或者中文名字,看官方文档叫它"The !! Operator",!!是什么鬼,My name is !!,手动滑稽。
    • 对于需要NPE的人,就要用到这个操作符了,当一个变量使用!!时,如果该变量是null,则会抛出NPE
      val temp: String = "fish_leong";
      temp!!.length//这时编译器会提示 Unnecessary noo-null assertion(!!) on a non-null receiver of type String,因为temp声明时不接收空引用,所以在此使用!!也是多余的
    
      var temp1: String? = temp;//定义一个可接收空引用的temp1
      println("第1次$temp1!!")//使用!!,此时temp1不为null
      println("第2次$temp1")//不使用!!,此时temp1不为null
      temp1 = null
      println("第3次$temp1")//不使用!!,此时temp1为null
      println("第4次$temp1!!")//使用!!,此时temp1为null,将会抛出NPE
    

    输出

    第1次fish_leong
    第2次fish_leong
    第3次null
    Exception in thread "main" kotlin.KotlinNullPointerException
      at HelloKt.main(Hello.kt:29)
    

参考文档:
[1]空安全 - Kotlin 语言中文站
[2]Null Safety - Kotlin Programming Language
[3]NullPointerException - 百度百科