• [Kotlin学习] 6. null 安全与异常


    第六章 null 安全与异常

    6.1 可空性

    • 可为空: 可以赋 null
    • 不可为空: 不能被赋 null
    fun main(args: Array<String>) {
        var signatureDrink = "Buttered Ale"
        signatureDrink = null
    }
    

    会报错:Null can not be a value of a non-null type String

    因为 String 变量属于非空类型

    而上述代码在 Java 中可行

    String signatureDrink = "Buttered Ale";
    signatureDrink = null;
    

    但是拼接一个字符串和 signatureDrink 空值变量会抛出 NullPointerException 的异常,因为一个不存在的东西无法拼接

    signatureDrink = signatureDrink + ", large";
    

    null 值表示变量不存在,而空字符串表示变量存在且值为 ""。所以空字符串可以拼接,而 null 不可以,会报错

    在允许任何类型为 null 的语言中,NullPointerException 是应用程序崩溃的最常见原因

    对于 null 值问题,除非另有规定,Kotlin 中变量不可为 null 值,这样运行时崩溃从根源上得到了解决

    6.2 Kotlin 的 null 类型

    readLine 函数从控制台获取用户输入,然后返回给应用程序

    public fun readLine(): String?
    

    String? 返回类型中的问号表示可空。这表示 readLine 要么返回一个 String 类型的值,要么返回 null

    6.3 编译时间与运行时间

    Kotlin 是一门编译型语言:Kotlin 应用代码先编译成机器语言指令,再由一个叫编译器的特殊程序执行

    在编译阶段,编译器会检查代码是否符合特定要求,确认没问题后再编译生成机器指令

    • 编译时错误: 在编译时捕获的错误
    • 运行时错误: 在运行时出现的错误

    通常来讲,编译时错误要好过运行时错误。写代码时就能发现问题显然要比运行时发现好

    6.4 null 安全

    操作一个可空变量,而它又可能不存在,为了应对这种风险,Kotlin 不允许在可空类型值上调用函数,除非主动接手安全管理

    fun main(args: Array<String>) {
        var beverage = readLine().capitalize()
        println(beverage)
    }
    

    上面的代码会出现编译时错误:

    Kotlin: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?
    

    不让调用 captalize 函数是因为没有考虑 beverage 变量为空的情况

    6.4.1 选项一:安全调用操作符

    在对一个可能为 null 的变量函数调用时,使用安全操作符 ?.,这样如果遇到 null 值,就会跳过函数调用

    fun main(args: Array<String>) {
        var beverage = readLine()?.capitalize()
        println(beverage)
    }
    

    使用带 let 的安全调用

    fun main(args: Array<String>) {
        var beverage = readLine()?.let {
            if (it.isNotBlank()) {
                it.capitalize()
            } else {
                "Buttered Ale"
            }
        }
        println(beverage)
    }
    

    6.4.2 选项二:使用 !!. 操作符

    !!. 操作符,如果为 null 会抛出 KotlinNullPointerException

    相比于安全调用操作符,!!. 操作符太激进,一般应避免使用

    !!. 的官方名字是非空断言操作符,大师开发人员更喜欢叫“感叹号操作符”

    fun main(args: Array<String>) {
        var beverage = readLine()!!.capitalize()
        println(beverage)
    }
    

    6.4.3 选项三:使用 if 判断 null 值情况

    fun main(args: Array<String>) {
        var beverage = readLine()
        if (beverage != null) {
            beverage = beverage.capitalize()
        } else {
            println("I can't do that without crashing - beverage was null!")
        }
        println(beverage)
    }
    

    使用空合并操作符

    ?: 如果左边的求值结果是 null 就使用右边的结果值

    fun main(args: Array<String>) {
        var beverage = readLine()
        if (beverage != null) {
            beverage = beverage.capitalize()
        } else {
            println("I can't do that without crashing - beverage was null!")
        }
        val beverageServed: String = beverage ?: "Buttered Ale"
        println(beverageServed)
    }
    

    合并空操作符也可以和 let 函数一起使用来代替 if/else 语句

    var beverage = readLine()
    beverage?.let {
        beverage = it.captialize()
    } ?: println("I can't do that without crashing - beverage was null!")
    

    6.5 异常

    6.5.1 抛出异常

    Kotlin 允许你主动示意有异常发生。这种行为又叫抛出一个异常,由throw操作符触发

    在众多异常里面,IllegalStateException 是最常见的一个

    import java.lang.IllegalStateException
    
    fun main(args: Array<String>) {
        var swordsJuggling: Int? = null
        val isJugglingProficient = (1..3).shuffled().last() == 3
        if (isJugglingProficient) {
            swordsJuggling = 2
        }
        proficiencyCheck(swordsJuggling)
        println("You juggle $swordsJuggling swords!")
    }
    
    fun proficiencyCheck(swordsJuggling: Int?) {
        swordsJuggling ?: throw IllegalStateException("Player cannot juggle swords")
    }
    

    6.5.2 自定义异常

    刚刚抛出的 IllegalStateException 表明程序出了不合法的状况

    为了提供更多细节,可以针对某类很特殊的问题创建自定异常

    class UnskilledSwordJugglerException() :
            IllegalStateException("Player cannot juggle swords")
    

    6.5.3 处理异常

    通过定义包裹有问题的代码的 try/catch 语句,Kotlin 可以让你决定如何来处理异常

    fun main(args: Array<String>) {
        var swordsJuggling: Int? = null
        val isJugglingProficient = (1..3).shuffled().last() == 3
        if (isJugglingProficient) {
            swordsJuggling = 2
        }
        try {
            proficiencyCheck(swordsJuggling)
            swordsJuggling = swordsJuggling!!.plus(1)
        } catch (e: Exception) {
            println(e)
        }
        println("You juggle $swordsJuggling swords!")
    }
    
    fun proficiencyCheck(swordsJuggling: Int?) {
        swordsJuggling ?: throw UnskilledSwordJugglerException()
    }
    
    class UnskilledSwordJugglerException() :
            IllegalStateException("Player cannot juggle swords")
    

    6.6 先决条件函数

    先决条件函数: 条件必须满足目标代码才能执行的函数

    前面已经介绍了好几种避免 KotlinNullPointerException 等异常的方法,使用先决条件函数也是一种方法

    checkNotNull 是一个先决条件函数,用来检查某个值是否为 null, 如果不是,就返回该值,反之就抛出 IllegalStateException 异常

    fun proficiencyCheck(swordsJuggling: Int?) {
        checkNotNull(swordsJuggling) { "Player cannot juggle swords" }
    }
    

    这 5 个先决条件函数中,require函数尤其有用。其他函数可以利用它指定自身值参的边界

    fun juggleSwords(swordsJuggling: Int) {
        require(swordsJuggling >= 3) {"Juggle at least 3 swords to be exciting."}
    }
    
  • 相关阅读:
    Sqlite框架Delphi10.3(07)
    FastReport 6.8.11在Delphi10.3上的安装
    Delphi 中,InputQuery 函数的妙用
    Javaday25(sax解析xml,json,设计模式)
    并发学习第五篇——volatile关键字
    并发学习第四篇——synchronized关键字
    并发学习第二篇——Thread
    并发学习第一篇——Runnable
    git-仓库迁移并保留commit log
    并发编程理论基础二——画图理解
  • 原文地址:https://www.cnblogs.com/huihao/p/15810749.html
Copyright © 2020-2023  润新知