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)
相关推荐
三氧化真6 分钟前
设计模式-装饰者模式
java·设计模式
抚月code7 分钟前
乐观锁、悲观锁及死锁
java
XXXJessie10 分钟前
c++278函数指针
java·c++·算法
GGBondlctrl17 分钟前
【后端开发】JavaEE初阶—Theard类及常见方法—线程的操作(超详解)
java·开发语言·thread类的构造方法·thread类的属性·启动线程·终止线程·等待线程
机器不会学习CL19 分钟前
时序预测|基于灰狼优化LightGBM的时间序列预测Matlab程序GWO-LightGBM 单变量和多变量 含基础模型
开发语言·神经网络·matlab·数据挖掘·回归·时间序列·智能优化
不会敲代码的VanGogh20 分钟前
【iOS】——JSONModel源码
开发语言·学习·ios·objective-c
落落落sss22 分钟前
项目集成sharding-jdbc
android·java·数据库·spring·mybatis
WolvenSec26 分钟前
C/C++逆向:switch语句逆向分析
c语言·开发语言·c++·安全·网络安全
T0uken29 分钟前
【LLM】Ollama:本地大模型 WebAPI 调用
开发语言·python
尘浮生29 分钟前
Java项目实战II基于Java+Spring Boot+MySQL的酒店客房管理系统(源码+数据库+文档)
java·开发语言·数据库·spring boot·mysql·spring·intellij-idea