Kotlin 抛出和捕获异常(十一)

导读大纲

    • [1.1 在 Kotlin 中抛出和捕获异常](#1.1 在 Kotlin 中抛出和捕获异常)
      • [1.1.1 处理异常和从错误中恢复: try、catch 和 finally](#1.1.1 处理异常和从错误中恢复: try、catch 和 finally)
      • [1.1.2 使用 try 作为表达式](#1.1.2 使用 try 作为表达式)

1.1 在 Kotlin 中抛出和捕获异常

  1. Kotlin 中的异常处理类似于 Java 和许多其他语言中的处理方式

    • 函数可以正常完成,也可以在发生错误时抛出异常
    • 函数的调用者 可以捕获异常并进行处理
      1. 如果不捕获,异常会被重新抛出到堆栈的更上层
  2. 可以使用 throw 关键字抛出异常

    • <1> 在本例中,表示调用函数提供一个无效的百分比值
      1. Kotlin 没有 new 关键词, 创建异常实例也是如此
kotlin 复制代码
if (percentage !in 0..100) {                 // <1>
    throw IllegalArgumentException(          // <1>
        "A percentage value must be between 0 and 100: $percentage"
    )
}
  1. 在 Kotlin 中,throw 结构是一个表达式
    • <1> throw 是一个表达式, 可作为其他表达式的一部分 使用
      1. 如果条件满足, 程序将正常运行
        • 用 number 初始化 percentage 变量
      2. 否则, 程序将抛出异常 , 变量不会被初始化
      3. 关于表达式与语句的区别--传送门
kotlin 复制代码
val percentage =
    if (number in 0..100)
        number
    else
        // <1>
        throw IllegalArgumentException("A percentage value must be between 0 and 100: $number"
)

1.1.1 处理异常和从错误中恢复: try、catch 和 finally

  1. 试图从错误中恢复,而不是抛出异常
    • 可以使用带有 catch 和 finally 子句的 try 结构处理异常
    • <1> 它从 BufferedReader 中读取一行文本 ,尝试将其解析为一个数字
      1. 如果该行不是一个有效的数字返回null
      2. 反之是有效数字, 则会返回解析后的数值
    • <2> 在 Kotlin 中, 您无法明确指定函数可以抛出的异常
kotlin 复制代码
import java.io.BufferedReader
import java.io.StringReader
fun readNumber(reader: BufferedReader): Int? {          // <2>
    try {
        val line = reader.readLine()                    // <1>
        return Integer.parseInt(line)                   // <1>
    } catch (e: NumberFormatException) {
        return null                                     // <1>
    } finally {
        reader.close()
    }
}
fun main() {
    val reader = BufferedReader(StringReader("239"))   // <1>
    println(readNumber(reader))
    // 239
}
  1. 与 Java 的一个重要区别 是,Kotlin 没有 throws 子句
    • <1> 如果用Java写这个函数,会在函数声明后明确写入throws IOException
      1. 在 Java 中,检查异常是方法签名的一部分
    • 您需要这样做,因为 Java 的 readLine 和 close 可能会抛出 IOException
      1. 这是一种经过检查 的异常

    • 在 Java 世界中,这是一种需要明确处理的异常类型
      1. 必须声明您的函数 显式 指定所有经过检查的异常*
    • 如果您调用另一个函数
      1. 需要处理它显式指定的异常声明函数也可以抛出这些异常
kotlin 复制代码
Integer readNumber(BufferedReader reader) throws IOException   // <1>
  1. 就像许多其他现代 JVM 语言一样, Kotlin 不区分检查异常和未检查异常

    • 您可以不指定函数抛出的异常 ,也可以不处理任何异常
      1. 这一设计决策 基于 Java 中使用检查(checked)异常的实践
    • 经验表明,Java 规则 往往需要大量无意义的代码重新抛出或忽略异常
      1. 这些规则并不能始终如一地保护免受可能发生的错误的影响
    • 例如,在上述示例中,NumberFormatException 并不是一个经过检查的异常
      1. 因此,Java编译器不会强制您捕获它
        • 你可以在运行时轻松地看到异常发生
      2. 这是很不幸的,因为无效的输入数据是一种常见的情况 ,应该从容应对
      3. 同时,BufferedReader.readLine方法抛出一个IOException异常
        • 这是一个经过检查的异常 ,需要加以处理
    • 如果流关闭,大多数程序都无法采取任何有意义的操作
      1. 因此捕获流操作方法的IOException 异常所需的代码都是模板化
  2. 由于这一设计决策,您可以自行决定要处理不要处理哪些异常

    • <1> 如果你愿意,完全可以不使用任何 try-catch 结构 来实现 readNumber 函数
      1. 在 Kotlin 中,编译器不会强制您处理异常
kotlin 复制代码
fun readNumber(reader: BufferedReader): Int {
    val line = reader.readLine()                     // <1>
    reader.close()
    return Integer.parseInt(line)
}
  1. 那么 Java 的 try-with-resources 呢?
    • Kotlin 对此没有任何特殊语法 , 它是作为库函数实现的

1.1.2 使用 try 作为表达式

  1. 到目前为止,你只看到 try 结构作为语句使用
    • 但是,由于 try 是一个表达式(就像 if 和 when 一样)
    • <1> 利用这一点,将 try 表达式的值赋值给一个变量
kotlin 复制代码
fun readNumber(reader: BufferedReader): Int? {
    val number = try {                                  // <1>
        Integer.parseInt(reader.readLine())
    }catch (e: NumberFormatException) {
        return null
    }finally {
        reader.close()
    }
}
  1. 值得注意的是,与 if 不同,即使块体只包含一个表达式,大括号也是不能省略
    • 与其他语句一样,如果主体包含多个表达式
      1. 整个 try 表达式的值 就是最后一个表达式的值
      2. 代码块中的最后一个表达式即为结果--传送门
    • 上面示例在 catch 代码块中加入 return 语句
      1. 因此函数不会在 catch 代码块之后继续执行
    • <1> 如果要继续执行,catch 子句也作为表达式
      1. 即这里的null作为catch表达式的值赋值给 number 变量
kotlin 复制代码
fun readNumber(reader: BufferedReader): Int? {
    val number = try {
        Integer.parseInt(reader.readLine())
    }catch (e: NumberFormatException) {
        null                                       // <1>
    }
    println("number: $number")
    return number
}
  1. 如果 try 代码块的执行情况正常,则该代码块中的最后一个表达式就是结果
    • 如果捕获异常,相应 catch 代码块中的最后一个表达式也是结果
    • 使用 try 作为表达式 ,可以避免引入额外的中间变量
      1. 还可以分配回退值(catch作为表达式)或直接从外层函数返回(在catch中return)
相关推荐
天天扭码1 分钟前
五天SpringCloud计划——DAY2之单体架构和微服务架构的选择和转换原则
java·spring cloud·微服务·架构
程序猿进阶2 分钟前
堆外内存泄露排查经历
java·jvm·后端·面试·性能优化·oom·内存泄露
FIN技术铺6 分钟前
Spring Boot框架Starter组件整理
java·spring boot·后端
zwjapple12 分钟前
typescript里面正则的使用
开发语言·javascript·正则表达式
小五Five13 分钟前
TypeScript项目中Axios的封装
开发语言·前端·javascript
小曲程序14 分钟前
vue3 封装request请求
java·前端·typescript·vue
前端每日三省15 分钟前
面试题-TS(八):什么是装饰器(decorators)?如何在 TypeScript 中使用它们?
开发语言·前端·javascript
凡人的AI工具箱28 分钟前
15分钟学 Go 第 60 天 :综合项目展示 - 构建微服务电商平台(完整示例25000字)
开发语言·后端·微服务·架构·golang
陈王卜31 分钟前
django+boostrap实现发布博客权限控制
java·前端·django
小码的头发丝、31 分钟前
Spring Boot 注解
java·spring boot