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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 12:45:49