我在设计一个通信类时遇到了一个泛型问题.

首先,有两种类型的操作:查询和变异.每一种的定义如下.

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
}

这些接口在library中,不能更改.

我可以 for each 查询和Mutations 创建以下Caller类.

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)
    }
}

有没有办法将这两个调用方集成到一个可以处理查询和Mutations 的Caller类中?

我想在下面这样做.但Operation<Data>不能强制转换为Query<Data>Mutation<Data>,因为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)
    }
}

仅供参考,在SWIFT中,我们可以使用where子句将查询和Mutations 的调用方法分开,如下所示.

struct OperationCaller<T: Operation<Data>, Data: Operation.Data, Model> {
    let operation: T
    let toModel: (Data) -> Model

    func call(client: ApolloClient): Model? where T: Query {
        guard let data = client.query(operation).execute().data else { return nil }
        return toModel(data)
    }

    func call(client: ApolloClient): Model? where T: Mutation {
        guard let data = client.mutation(operation).execute().data else { return nil }
        return toModel(data)
    }
}

最后,这些示例代码是一个简化版本;实际上,它稍微复杂一些.

推荐答案

只需将SWIFT代码编写为扩展函数,即可执行类似于SWIFT代码的操作.

其 idea 是,扩展函数可以将T的通用约束细化到Query<Data>Mutation<Data>,而在SWIFT中,这可以内联到where.

class OperationCaller<T: Operation<Data>, Data: Operation.Data, Model>(
    val operation: T,
    val toModel: (Data) -> Model
) // this can be an empty class

@JvmName("callQuery")
fun <T: Query<Data>, Data: Query.Data, Model> OperationCaller<T, Data, Model>.call(client: ApolloClient): Model? {
    val data = client.query(operation).execute().data ?: return null
    return toModel(data)
}

@JvmName("callMutation")
fun <T: Mutation<Data>, Data: Query.Data, Model> OperationCaller<T, Data, Model>.call(client: ApolloClient): Model? {
    val data = client.mutation(operation).execute().data ?: return null
    return toModel(data)
}

Kotlin相关问答推荐

如何在 Big Data 中使用Inc过滤器?

Kotlin中函数引用的否定

可以从背景图像中点击图标吗?

Kotlin supervisorScope 即使包裹在 try catch 中也会失败

为什么这两个 Kotlin 协程函数不一样?

Kotlin 可空泛型

jlink:在合并模块和 kotlin.stdlib 中打包 kotlin.*

为什么多线程不会使执行更快

如何在 Android Jetpack Compose 中的画布上绘制一侧加厚的描边?

从 Kotlin 调用 Java 时可以为空的规则是什么

在 Kotlin 中使用 @Parcelize 注释时如何忽略字段

从列表中的每个对象中 Select 属性

来自类型参数的属性的自定义 getter

Kotlin 中 lambda 表达式中的默认参数

API 'variant.getJavaCompile()' 已过时

Kotlin协程处理错误和实现

Jetpack Compose-居中文本

在 Kotlin 中创建非绑定服务

Recyclerview: listen to padding click events

从另一个列表创建一个列表