如何在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
相关产品推荐
相关产品推荐

