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

Kotlin中能否在方法内调整泛型类参数边界并整合调用类?

Kotlin泛型整合Query/Mutation调用类的解决方案

问题背景

现有第三方库定义的Operation接口体系:

interface Operation<D : Operation.Data> : Executable<D> {
    interface Data : Executable.Data
}

interface Query<D : Query.Data> : Operation<D> {
  interface Data: Operation.Data
}

interface Mutation<D : Mutation.Data> : Operation<D> {
  interface Data: Operation.Data
}

已实现的QueryCaller和MutationCaller:

class QueryCaller<T: Query<Data>, Data: Query.Data, Model>(
    val operation: T,
    val toModel: (Data) -> Model
) {
    suspend fun call(client: ApolloClient): Model? {
        val data = client.query(operation).execute().data ?: return null
        return toModel(data)
    }
}

class MutationCaller<T: Mutation<Data>, Data: Mutation.Data, Model>(
    val operation: T,
    val toModel: (Data) -> Model
) {
    suspend fun call(client: ApolloClient): Model? {
        val data = client.mutation(operation).execute().data ?: return null
        return toModel(data)
    }
}

尝试整合为OperationCaller时,因泛型类型边界不匹配,无法安全将Operation<Data>转换为Query<Data>或Mutation<Data>:

class OperationCaller<T: Operation<Data>, Data: Operation.Data, Model>(
    val operation: T,
    val toModel: (Data) -> Model
) {
    suspend fun call(client: ApolloClient): Model? {
        val query = operation as? Query<Data> // 类型转换不安全且可能失败
        val mutation = operation as? Mutation<Data>

        val data = if (query != null) {
            client.query(query).execute().data
        } else if (mutation != null) {
            client.mutation(mutation).execute().data
        } ?: return null

        return toModel(data)
    }
}

解决方案

1. 使用方法级泛型约束(模拟Swift的where子句)

Kotlin支持为函数单独定义泛型约束,通过where关键字为不同操作类型实现针对性的call方法,编译器会自动匹配对应类型:

class OperationCaller<T : Operation<D>, D : Operation.Data, Model>(
    val operation: T,
    val toModel: (D) -> Model
) {
    // 处理Query类型的调用,约束T必须是Query<D>且D是Query.Data
    suspend fun call(client: ApolloClient): Model? where T : Query<D>, D : Query.Data {
        val data = client.query(operation).execute().data ?: return null
        return toModel(data)
    }

    // 处理Mutation类型的调用,约束T必须是Mutation<D>且D是Mutation.Data
    suspend fun call(client: ApolloClient): Model? where T : Mutation<D>, D : Mutation.Data {
        val data = client.mutation(operation).execute().data ?: return null
        return toModel(data)
    }
}

使用时无需手动类型转换,完全保证类型安全,编译器会根据传入的operation类型自动选择对应方法。

2. 单一call方法的类型安全实现(可选)

若希望保留单一call方法,可通过具体化泛型+类型检查实现安全转换,需将方法定义为inline函数:

class OperationCaller<T : Operation<D>, D : Operation.Data, Model>(
    val operation: T,
    val toModel: (D) -> Model
) {
    suspend inline fun call(client: ApolloClient): Model? {
        val data = when(operation) {
            is Query<*> -> client.query(operation as Query<D>).execute().data
            is Mutation<*> -> client.mutation(operation as Mutation<D>).execute().data
            else -> null
        } ?: return null
        
        return toModel(data)
    }
}

此方案兼容性更强,但可读性略逊于第一种方法。

关于方法内调整类型参数边界的问题

Kotlin无法直接修改类级别泛型参数的边界,但可以在方法上定义独立的泛型参数,并为这些参数添加更严格的约束(如上述方案中的where子句)。通过这种方式,方法可以针对类泛型的特定子类型进行处理,实现类似Swift中where子句的效果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 13:52:51