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

