Kotlin协程多API并发调用及泛型类型问题解决
Kotlin协程多API并发调用的泛型问题解决
问题背景
原本基于Kotlin协程实现了单API调用逻辑(通过inline函数返回成功或错误结果),现在需要扩展为多API并发调用:初始化多个任务异步执行,等待所有任务完成后返回失败任务列表用于重试。但定义ApiTask数据类时遇到泛型困境:
- 直接使用泛型
T会触发编译错误 - 改用
JsonResponse类型的话,onSuccess回调无法获取apiBlock返回的具体子类实例(比如继承自JsonResponse的LoginResponse)
单API调用可行代码
protected fun<T: JsonResponse> apiCall(apiBlock: suspend CoroutineScope.() -> T, onError: ((ApiCallError)->Unit)? = null, onDone: (()->Unit)? = null, onSuccess: suspend (T)->Unit): Job { return launch(Dispatchers.Main){ val (r, err) = withContext(Dispatchers.IO){ try { apiBlock() to null } catch (e: ApiCallError) { null to e } } onDone?.invoke() when { r != null -> onSuccess(r) err != null -> { onError?.invoke(err) } } } }
最初尝试的多API调用代码(存在泛型问题)
data class ApiTask( val apiBlock: suspend CoroutineScope.() -> T, val onSuccess: suspend (T)->Unit) protected fun<T: JsonResponse> apiCallChained( apiBlocks: List<ApiTask>, onError: ((List<ApiTask>)->Unit), onSuccess: ()->Unit){ val failedTasks = mutableListOf<ApiTask>() apiBlocks.forEach { apiBlock-> launch(Dispatchers.Main){ val (r, err) = withContext(Dispatchers.IO){ try { apiBlock.apiBlock(this) to null } catch (e: ApiCallError) { null to e } } when { r != null -> { apiBlock.onSuccess(r) } err != null -> failedTasks.add(apiBlock) } } } if (failedTasks.isEmpty()) onSuccess.invoke() else onError.invoke(failedTasks) }
API实现示例
class ApiLogin(js: JSONObject): JsonResponse(js) { companion object { @Throws(ApiCallError::class) operator fun invoke(api: AppApi, email: String, pass: String): ApiLogin{ return ApiLogin(api.apiLoginUser(email, pass)) } } class LoginServerResponse(js: JSONObject): JsonResponse(js){ val httpCode by JsInt("httpCode") val session by JsString("session") } val r = LoginServerResponse(js) }
修复后的ApiTask数据类
通过协变泛型和@UnsafeVariance注解解决泛型冲突:
data class ApiTask<out T: JsonResponse>( val apiBlock: suspend CoroutineScope.() -> T, val onSuccess: suspend (@UnsafeVariance T)->Unit)
修复说明
- 协变泛型
out T:声明ApiTask的泛型T为协变类型,允许ApiTask<LoginResponse>作为ApiTask<JsonResponse>的子类使用,这样不同具体类型的API任务可以放入同一个列表统一处理 @UnsafeVariance注解:onSuccess回调需要接收T类型参数(属于逆变位置),而协变泛型默认禁止在逆变位置使用该泛型。这个注解告知编译器此处的用法是安全的(前提是不在回调中修改T的内部状态)
内容的提问来源于stack exchange,提问作者Martin
相关产品推荐
相关产品推荐

