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
相关产品推荐
相关产品推荐

