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

Kotlin Retrofit泛型转换报错:LinkedTreeMap无法转为LogonResponse

问题原因

  • 泛型类型擦除是核心问题:Kotlin/Java的泛型仅在编译期有效,运行时BaseResponse<T>中的T会被擦除为Object。你在解析时创建的TypeToken<BaseResponse<T>>(),运行时无法获取T的具体类型(这里是LogonResponse),Gson只能将data字段默认解析为LinkedTreeMap,后续强转LogonResponse自然抛出类型转换异常。
  • 虽然ApiCallback<LogonResponse>声明了泛型,但该信息仅在编译期存在,运行时已丢失,Gson解析阶段无法利用它生成具体的LogonResponse实例。

解决方案

方案1:利用Kotlin inline+reified泛型(推荐)

通过inline函数保留泛型的运行时信息,让Gson能直接解析出正确类型:

// 封装解析逻辑
inline fun <reified T> parseBaseResponse(json: String): BaseResponse<T> {
    val type = object : TypeToken<BaseResponse<T>>() {}.type
    return Gson().fromJson(json, type)
}

// 修改logOn方法为inline泛型方法
inline fun <reified T> logOn(@Body requestBody: RequestBody, listener: ApiCallback<T>) {
    Call.enqueue(object : Callback<ResponseBody> {
        override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
            if (response.isSuccessful) {
                val decodedString = String(Base64.decode(response.body()!!.string(), Base64.DEFAULT))
                // 直接用reified泛型解析
                val baseMSResponse = parseBaseResponse<T>(decodedString)
                listener.success(baseMSResponse)
            }
        }
    })
}

调用时无需额外传递类型信息:

MyService.Account.logOn<LogonResponse>(request, object : ApiCallback<LogonResponse> {
    override fun success(body: BaseResponse<LogonResponse>) {
        Log.d("TAG", " logOn success=" + body.data!!.token)
    }
    override fun fail(message: String) {}
})

方案2:让ApiCallback携带具体类型信息

修改ApiCallback接口,添加获取Type的方法,让解析时能拿到具体泛型类型:

interface ApiCallback<T> {
    fun success(body: BaseResponse<T>)
    fun fail(message: String)
    // 返回当前泛型对应的TypeToken
    fun getResponseType(): Type = object : TypeToken<BaseResponse<T>>() {}.type
}

解析时使用该Type:

Call.enqueue(object : Callback<ResponseBody> {
    override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
        if (response.isSuccessful) {
            val decodedString = String(Base64.decode(response.body()!!.string(), Base64.DEFAULT))
            val baseMSResponse = Gson().fromJson<BaseResponse<T>>(decodedString, listener.getResponseType())
            listener.success(baseMSResponse)
        }
    }
})

为什么二次解析能成功?

当你调用body.data.toString()时,实际上是把LinkedTreeMap序列化为JSON字符串,之后显式指定解析目标为LogonResponse,Gson此时能明确知道要生成的类型,因此解析成功。但这种方式多了一次序列化/反序列化操作,存在性能损耗,不推荐作为常规方案。

内容的提问来源于stack exchange,提问作者GHH

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 21:59:58