0Pricing
Kotlin Academy · 课时

密封的 Result 层次结构

自定义结果类型

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

超越内置 Result

Kotlin 的 Result 只会在 failure 时携带异常。很多时候,您需要更丰富且类型明确的错误信息。密封类层次结构可以准确地表示您的领域所具有的各种结果。

密封的 Outcome

定义一个包含 success 和错误子类的密封类型。编译器知道所有情况,因此可以进行穷尽式处理。

sealed class Outcome<out T> {
    data class Ok<T>(val value: T) : Outcome<T>()
    data class Err(val message: String) : Outcome<Nothing>()
}

fun main() {
    val r: Outcome<Int> = Outcome.Ok(5)
    println(r)
}

穷尽式条件分支

由于该类型是密封的,对它使用 when 时不需要 else,编译器也会强制您处理每一种情况。

sealed class Outcome<out T> {
    data class Ok<T>(val value: T) : Outcome<T>()
    data class Err(val message: String) : Outcome<Nothing>()
}

fun describe(o: Outcome<Int>) = when (o) {
    is Outcome.Ok -> "value " + o.value
    is Outcome.Err -> "error " + o.message
}

fun main() {
    println(describe(Outcome.Ok(9)))
    println(describe(Outcome.Err("boom")))
}

类型化错误变体

将不同的 failure 类型建模为独立的子类,每个子类都携带相关数据。这比使用单个不透明的异常更好。

sealed class Fetch<out T> {
    data class Success<T>(val data: T) : Fetch<T>()
    data class NotFound(val id: Int) : Fetch<Nothing>()
    data class Network(val code: Int) : Fetch<Nothing>()
}

fun main() {
    val r: Fetch<String> = Fetch.NotFound(42)
    println(r)
}

处理每个变体

when 可以针对每种错误类型做出具体反应,并且每个分支都能完整访问相应数据。

sealed class Fetch<out T> {
    data class Success<T>(val data: T) : Fetch<T>()
    data class NotFound(val id: Int) : Fetch<Nothing>()
    data class Network(val code: Int) : Fetch<Nothing>()
}

fun handle(f: Fetch<String>) = when (f) {
    is Fetch.Success -> "data: " + f.data
    is Fetch.NotFound -> "missing id " + f.id
    is Fetch.Network -> "net error " + f.code
}

fun main() {
    println(handle(Fetch.Network(503)))
}

Nothing 技巧

不携带 success 值的错误变体使用 Outcome<Nothing>。由于 Nothing 是每种类型的子类型,并且泛型参数是 out,错误就可以适配任何 Outcome<T>。

sealed class Res<out T> {
    data class Ok<T>(val v: T) : Res<T>()
    object Empty : Res<Nothing>()
}

fun get(flag: Boolean): Res<String> =
    if (flag) Res.Ok("hi") else Res.Empty

fun main() {
    println(get(false))
}

无状态情形使用单例对象

如果错误变体不携带任何数据,请将其声明为 object 单例,而不是类。

sealed class Login {
    data class Success(val user: String) : Login()
    object WrongPassword : Login()
    object Locked : Login()
}

fun main() {
    val r: Login = Login.WrongPassword
    println(r === Login.WrongPassword)
}

返回层次结构

函数返回密封类型;调用方必须处理每一种结果,从而消除被遗忘的错误路径。

sealed class Parsed {
    data class Num(val value: Int) : Parsed()
    data class Bad(val input: String) : Parsed()
}

fun parse(s: String): Parsed {
    val n = s.toIntOrNull()
    return if (n != null) Parsed.Num(n) else Parsed.Bad(s)
}

fun main() {
    when (val r = parse("x")) {
        is Parsed.Num -> println(r.value)
        is Parsed.Bad -> println("bad: " + r.input)
    }
}

密封类型与内置 Result

对于快速的、基于异常的捕获,请使用内置的 Result。当您需要包含领域特定数据的多个类型化错误情况以及穷尽式处理时,请使用自定义的密封层次结构。

设计层次结构

良好的密封结果设计包括:

  • 一个携带有效数据的 success 变体
  • 每种有意义的 failure 模式对应一个子类
  • 使用 out T 变型,并使用 Nothing 表示仅包含错误的情况

编译器作为安全网

最大的好处是:以后添加新变体时,每个不完整的 when 都会变成编译错误,并指出每个必须处理它的位置。错误无法悄无声息地漏掉。

快速检查

请测试您对密封结果层次结构的理解。

回顾

您构建了密封结果层次结构:

  • success 加上类型化错误变体
  • 不使用 else 的穷尽式 when
  • 使用 out T 和 Nothing 表示仅包含错误的情况
  • 由编译器强制处理新变体

下一步:使用 map 和 recover 进行函数式错误处理。

常见问题解答

「密封的 Result 层次结构」课时是免费的吗?

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

「密封的 Result 层次结构」这节课中我会学到什么?

自定义结果类型 你通过在浏览器中直接运行的动手代码来练习 Kotlin Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Kotlin Academy 需要有经验吗?

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

「密封的 Result 层次结构」课时需要多长时间?

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

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

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

此课程中的所有课时

  1. Result 类型
  2. runCatching
  3. 密封的 Result 层次结构
  4. 函数式错误处理
← 返回 Kotlin Academy