
本文介绍如何在 Kotlin 中为 Apollo Android 客户端编写类型安全的通用查询函数,解决因类型擦除导致的 Response 泛型退化问题,使自定义封装函数能完整保留原始查询的 Response 精确类型。
本文介绍如何在 kotlin 中为 apollo android 客户端编写类型安全的通用查询函数,解决因类型擦除导致的 `response
在使用 Apollo Android 进行 GraphQL 请求时,我们常希望将重复的 apolloClient.query(...) 调用抽象为一个通用工具函数。但若直接使用通配符泛型(如 Query),Kotlin 编译器会丢失具体类型信息,导致返回类型退化为 Response<out any></out>,丧失编译期类型安全与 IDE 智能提示能力——这正是原问题中 response2 无法获得 Response<mytestquery.data></mytestquery.data> 类型的根本原因。
关键在于:Apollo 的 Query 接口定义为 Query<d : operation.data t v operation.variables></d>,其中:
-
D是响应数据的具体类型(如MyTestQuery.Data), -
T是序列化后的目标类型(通常与D一致,由Query#toResponse()决定), -
V是变量类型(如MyTestQuery.Variables)。
因此,通用函数必须显式绑定这三个泛型参数,并将其传导至返回类型 ApolloQueryCall<t></t>(注意:ApolloQueryCall 的类型参数对应 Response<t></t> 中的 T,而非 Data):
private fun <d : operation.data t v operation.variables>
getCallFrom(query: Query<d t v>): ApolloQueryCall<t> = apolloClient.query(query)</t></d></d>
✅ 正确调用示例:
fun test() {
// 原生调用:类型精确 → Response<mytestquery.data>
val response1 = apolloClient.query(MyTestQuery("test")).await()
// 通用函数调用:类型同样精确 → Response<mytestquery.data>
val response2 = getCallFrom(MyTestQuery("test")).await()
}</mytestquery.data></mytestquery.data>
⚠️ 注意事项:
- 不要省略
D和V的上界约束(D : Operation.Data,V : Operation.Variables),否则编译器无法推断类型关系; - 返回类型必须是
ApolloQueryCall<t></t>(而非ApolloQueryCall<out any></out>或ApolloQueryCall<d></d>),因为ApolloQueryCall<t></t>的execute()/await()方法返回Response<t></t>,而T才是实际解析后的数据类型(通常与D相同,但 Apollo 允许自定义CustomTypeAdapters导致T ≠ D); - 若需进一步封装
await()逻辑(如统一错误处理),可扩展为内联函数并添加suspend修饰符,但需注意协程上下文传递。
总结:泛型函数不是“越宽泛越好”,而是要精准建模 API 的类型契约。通过显式声明并约束 Query 的三个类型参数,我们让 Kotlin 编译器得以完整推导出 Response<t></t> 的 T,从而在保持代码复用性的同时,不牺牲任何类型安全性与开发体验。










