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)
相关推荐
FAQEW3 分钟前
Spring boot 中的IOC容器对Bean的管理
java·spring boot·后端·bean·ioc容器
dpxiaolong8 分钟前
RK3588平台用v4l工具调试USB摄像头实践(亮度,饱和度,对比度,色相等)
android·windows
0509159 分钟前
测试基础笔记第十一天
java·数据库·笔记
只可远观11 分钟前
Flutter Dart 循环语句 for while do..while break、continue
开发语言·javascript·ecmascript
IDRSolutions_CN37 分钟前
如何将 PDF 中的文本提取为 JSON 格式
java·经验分享·pdf·软件工程·团队开发
摘星编程1 小时前
并发设计模式实战系列(6):读写锁
java·设计模式·并发编程
吴_知遇1 小时前
【华为OD机试真题】428、连续字母长度 | 机试真题+思路参考+代码解析(E卷)(C++)
开发语言·c++·华为od
tangweiguo030519871 小时前
Android 混合开发实战:统一 View 与 Compose 的浅色/深色主题方案
android
老狼孩111221 小时前
2025新版懒人精灵零基础及各板块核心系统视频教程-全分辨率免ROOT自动化开发
android·机器人·自动化·lua·脚本开发·懒人精灵·免root开发
Java中文社群1 小时前
最火向量数据库Milvus安装使用一条龙!
java·人工智能·后端