0Pricing
Kotlin Academy · 课时

重新抛出异常与异常链

有选择地重新抛出异常,并通过原因链包装底层错误。

重新抛出异常与异常链 是 CoddyKit 上的免费 Kotlin Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Kotlin Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Kotlin Academy 课程共包含 4 节课。

重新抛出与包装

有时捕获异常只是为了在让更具体的异常继续传播之前对其进行记录、包装或转换。Kotlin 让这种写法十分自然。

直接重新抛出

要重新抛出异常,只需再次执行 throw e,异常就会继续传播到下一个处理器。

fun main() {
    try {
        try { "x".toInt() }
        catch (e: NumberFormatException) {
            println("logging: ${e.message}")
            throw e // re-throw original
        }
    } catch (e: NumberFormatException) {
        println("outer caught: ${e.message}")
    }
}

使用自定义异常进行包装

创建新异常时,将原始异常作为 cause 传入,以保留异常链。

class ParseException(msg: String, cause: Throwable) : RuntimeException(msg, cause)
fun main() {
    try {
        try { "xyz".toInt() }
        catch (e: NumberFormatException) {
            throw ParseException("could not parse user input", e)
        }
    } catch (e: ParseException) {
        println("got: ${e.message}")
        println("cause: ${e.cause?.message}")
    }
}

检查原因链

cause 属性链接到前一个异常。沿着该链接遍历即可打印完整的根本原因。

fun main() {
    val e = RuntimeException("top",
        RuntimeException("middle",
            RuntimeException("root")))
    var current: Throwable? = e
    while (current != null) {
        println(current.message)
        current = current.cause
    }
}

选择性重新抛出

捕获宽泛类型,然后在它不匹配预期情况时重新抛出。

fun parseRequired(s: String): Int {
    try {
        return s.toInt()
    } catch (e: NumberFormatException) {
        if (s.isBlank()) return 0
        throw e // unexpected — let it propagate
    }
}
fun main() {
    println(parseRequired("42"))
    println(parseRequired(""))
    // parseRequired("abc") // throws
}

添加上下文信息

使用领域异常进行包装,以添加原始异常缺少的上下文信息,例如输入、请求标识等。

class UploadError(msg: String, cause: Throwable) : RuntimeException(msg, cause)
fun upload(file: String) {
    try { throw RuntimeException("disk full") }
    catch (e: Throwable) { throw UploadError("uploading $file failed", e) }
}
fun main() {
    try { upload("photo.jpg") }
    catch (e: UploadError) { println("${e.message} <- ${e.cause?.message}") }
}

不要丢失堆栈跟踪

包装异常时,始终将原始异常作为 cause 传入,不要只创建一个具有相同消息但没有原因的新异常。堆栈跟踪是调试时不可或缺的线索。

class Wrapped(m: String, cause: Throwable) : RuntimeException(m, cause)
fun main() {
    try {
        try { error("inner") }
        catch (e: Throwable) { throw Wrapped("outer", e) }
    } catch (e: Wrapped) {
        println("message: ${e.message}")
        println("cause: ${e.cause?.message}")
    }
}

不包装而重新抛出

如果只需要记录日志,请在记录日志后执行 throw e,无需进行包装。

fun risky() {
    try { throw IllegalStateException("oops") }
    catch (e: IllegalStateException) {
        println("Logging before rethrow: ${e.message}")
        throw e
    }
}
fun main() {
    try { risky() }
    catch (e: IllegalStateException) { println("outer: ${e.message}") }
}

重新抛出期间使用 finally 进行清理

使用 finally 清理资源,即使正在重新抛出异常也能确保清理执行。

fun process() {
    try {
        throw RuntimeException("failure")
    } finally {
        println("cleanup ran")
    }
}
fun main() {
    try { process() }
    catch (e: RuntimeException) { println("caught: ${e.message}") }
}

被抑制的异常

如果 finally 自身抛出异常,原始异常默认会被抑制。使用 addSuppressed 可以保留两个异常。

fun main() {
    val primary = RuntimeException("primary")
    val secondary = RuntimeException("from cleanup")
    primary.addSuppressed(secondary)
    println(primary.suppressed.joinToString { it.message ?: "?" })
}

反模式:吞掉异常后抛出其他异常

捕获一个异常后抛出一个无关的新异常(且不带原因)会破坏调试信息。请始终传递 cause。

// BAD
// catch (e: IOException) { throw RuntimeException("error") }
// GOOD
// catch (e: IOException) { throw RuntimeException("error reading config", e) }
fun main() { println("Pass cause when re-throwing!") }

快速检查

将一个异常包装在新异常中时,您应该怎么做才能保留原始堆栈跟踪?

回顾

重新抛出:throw e。包装异常:使用 cause = e 抛出一个新异常。请始终保留原因链——不要构造不带原因的包装异常。请使用 finally 执行清理,并在清理本身失败时使用 addSuppressed。

常见问题解答

「重新抛出异常与异常链」课时是免费的吗?

是的 — 「重新抛出异常与异常链」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Kotlin Academy 课程的其余内容,请升级到 CoddyKit PRO。 Kotlin Academy 课程共包含 4 节课。

「重新抛出异常与异常链」这节课中我会学到什么?

有选择地重新抛出异常,并通过原因链包装底层错误。 你通过在浏览器中直接运行的动手代码来练习 Kotlin Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Kotlin Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Kotlin Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「重新抛出异常与异常链」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Kotlin Academy 课中编写并运行代码吗?

能。每节 Kotlin Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 作为表达式的 try/catch/finally
  2. 创建自定义异常类
  3. runCatching 与 Result
  4. 重新抛出异常与异常链
← 返回 Kotlin Academy