如何在Kotlin中结合Reified、泛型与接口实现JSON解码?
问题描述
我创建了一个使用Gson处理JSON序列化的Kotlin项目,核心代码如下:
import com.google.gson.Gson import com.google.gson.reflect.TypeToken interface Serializer { fun <T> decodeValue(data: String): T? } class JsonSerializer : Serializer { override fun <T> decodeValue(data: String): T? { try { val type = object : TypeToken<T>() {}.type val gson = Gson() return gson.fromJson<T>(data, type) } catch (e: Exception) { println("Error when parse: ${e.message}") } return null } } class Request<T>(val r: T) inline fun <reified T> callSerializer(json: String): T? { val serializer = JsonSerializer() val decoded = serializer.decodeValue<Request<T>>(json) return decoded?.r } fun main() { val finalValue = callSerializer<Request<String>>("{\"r\": \"test\"}") println("Decoded data is: $finalValue") }
Request类包含一个泛型类型的内部属性r- 我尝试将JSON数据
{"r": "test"}转换为Request<String>类型,但运行时抛出类型转换异常:
> Task :run FAILED Exception in thread "main" java.lang.ClassCastException: class com.google.gson.internal.LinkedTreeMap cannot be cast to class Request (com.google.gson.internal.LinkedTreeMap and Request are in unnamed module of loader 'app') at MainKt.main(Main.kt:36) at MainKt.main(Main.kt)
Gson把JSON解析成了LinkedTreeMap而非Request类,请问该如何解决?
问题原因及解决方法
原因
问题出在Serializer接口的decodeValue方法中,由于Java/Kotlin的类型擦除机制,当你在非具体化泛型函数里使用TypeToken<T>()时,Gson无法获取到Request<T>的具体泛型类型信息,只能把JSON解析成默认的LinkedTreeMap。
解决方法
有两种实用的修复方式:
方法1:让decodeValue支持传入类型令牌
修改Serializer接口和实现类,允许调用方传入具体的TypeToken,让Gson能准确识别目标类型:
import com.google.gson.Gson import com.google.gson.reflect.TypeToken interface Serializer { fun <T> decodeValue(data: String, typeToken: TypeToken<T>): T? } class JsonSerializer : Serializer { private val gson = Gson() // 提前初始化Gson,避免重复创建实例 override fun <T> decodeValue(data: String, typeToken: TypeToken<T>): T? { return try { gson.fromJson(data, typeToken.type) } catch (e: Exception) { println("Error when parse: ${e.message}") null } } } class Request<T>(val r: T) inline fun <reified T> callSerializer(json: String): T? { val serializer = JsonSerializer() // 传入具体的Request<T>类型令牌 val typeToken = object : TypeToken<Request<T>>() {} val decoded = serializer.decodeValue(json, typeToken) return decoded?.r } fun main() { // 泛型参数只需传String,callSerializer已处理Request的包装逻辑 val finalValue = callSerializer<String>("{\"r\": \"test\"}") println("Decoded data is: $finalValue") }
方法2:简化实现,直接在泛型函数中处理
如果不需要抽象的Serializer接口,可以直接利用reified特性让Gson获取准确类型:
import com.google.gson.Gson import com.google.gson.reflect.TypeToken class Request<T>(val r: T) private val gson = Gson() inline fun <reified T> callSerializer(json: String): T? { val typeToken = object : TypeToken<Request<T>>() {} return try { gson.fromJson<Request<T>>(json, typeToken.type)?.r } catch (e: Exception) { println("Error when parse: ${e.message}") null } } fun main() { val finalValue = callSerializer<String>("{\"r\": \"test\"}") println("Decoded data is: $finalValue") }
额外说明
- 修改后的
main函数泛型参数从Request<String>改为String,更贴合callSerializer获取内部属性r的语义 - 提前初始化
Gson实例,避免每次解析重复创建对象,提升性能
内容的提问来源于stack exchange,提问作者Paulo Coutinho
相关产品推荐
相关产品推荐

