You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Kotlin中创建支持多结果的广义代数数据类型(GADT)?

在Kotlin中实现带多返回结果的GADT风格Response

你原来的代码问题在于,Pending作为无状态单例(data object)无法持有外部接口的泛型参数<T>,而Error也不需要依赖T,Kotlin要求每个子类必须明确实现接口的具体类型参数。以下是可行的解决方案:

正确的实现方式

利用Kotlin的协变泛型和Nothing类型来适配不同状态的类型需求:

sealed interface Response<out T> {
    // Pending状态不携带数据,用Nothing作为类型参数(Nothing是所有类型的子类型)
    data object Pending : Response<Nothing>
    // Ok状态携带具体类型T的数据,用协变out允许向上转型
    data class Ok<out T>(val result: T) : Response<T>
    // Error状态只携带错误信息,同样用Nothing适配任意Response<T>
    data class Error(val reason: String) : Response<Nothing>
}

为什么这样可行?

  • 协变out:声明Response<out T>意味着如果A是B的子类型,那么Response<A>也是Response<B>的子类型。这让Response<Nothing>可以安全地赋值给任何Response<T>类型的变量(因为Nothing是所有类型的子类型)。
  • Nothing类型:作为Kotlin的底部类型,它适合用来标记“不返回任何具体数据”的状态,完美匹配Pending和Error这类不需要泛型参数的场景。

使用示例

fun fetchUser(): Response<String> {
    return Pending // 合法,Response<Nothing>可转为Response<String>
    // return Ok("Alice")
    // return Error("Failed to fetch user")
}

fun handleResponse(response: Response<String>) {
    when (response) {
        is Response.Pending -> println("Loading...")
        is Response.Ok -> println("Got result: ${response.result}")
        is Response.Error -> println("Error: ${response.reason}")
    }
}

更复杂的GADT场景

如果需要更精细的类型约束(比如不同状态对应不同的类型),Kotlin同样支持:

sealed interface Expr<out T> {
    data class IntLit(val value: Int) : Expr<Int>
    data class StrLit(val value: String) : Expr<String>
    data class Add(val left: Expr<Int>, val right: Expr<Int>) : Expr<Int>
}

fun evaluate(expr: Expr<*>): Any? {
    return when (expr) {
        is Expr.IntLit -> expr.value
        is Expr.StrLit -> expr.value
        is Expr.Add -> evaluate(expr.left) as Int + evaluate(expr.right) as Int
    }
}

内容的提问来源于stack exchange,提问作者Carlos Saltos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 02:03:12