Gson泛型反序列化报错:LinkedTreeMap转MyClass类型转换异常
问题代码
public static void main(String[] args) { String tr = "[{\"key\":\"foo\"}, {\"key\":\"shoe\"}]"; List<MyClass> o = new Gson().fromJson(tr, getType()); for (MyClass object : o) { System.out.println(object); } } public static <T extends MyClass> Type getType() { return new TypeToken<List<T>>() { }.getType(); } public static class MyClass { private String key; public String getKey() { return key; } public void setKey(String key) { this.key = key; } @Override public String toString() { return "X{" + "key='" + key + '\'' + '}'; } }
抛出的异常
Exception in thread "main" java.lang.ClassCastException: class com.google.gson.internal.LinkedTreeMap cannot be cast to class org.example.Main$MyClass (com.google.gson.internal.LinkedTreeMap and org.example.Main$MyClass are in unnamed module of loader 'app')
问题原因
你对类型擦除的理解存在误区:虽然getType方法声明了<T extends MyClass>,但这个T的具体类型在运行时会被完全擦除,而且Gson的TypeToken无法从方法的泛型参数中获取到实际的类型信息。
当调用getType()时,编译器无法推断出T的具体类型(即使左边变量是List<MyClass>,这种上下文无法传递到泛型方法的类型参数中),最终T会被当作Object处理。TypeToken<List<T>>实际生成的是List<Object>的类型,Gson解析JSON数组时,因为不知道具体的元素类型,就会用默认的LinkedTreeMap来存储每个JSON对象,后续强转MyClass自然就会抛出类型转换异常。
解决方案
有几种常见的修复方式:
直接在调用处创建
TypeToken
最直接的做法,把TypeToken的创建放在能明确指定泛型类型的地方:List<MyClass> o = new Gson().fromJson(tr, new TypeToken<List<MyClass>>(){}.getType());让
getType方法接收Class<T>参数,动态生成参数化类型
通过ParameterizedType手动构建List<T>的类型,避免依赖方法泛型参数的擦除:public static <T extends MyClass> Type getType(Class<T> clazz) { return new ParameterizedType() { @Override public Type[] getActualTypeArguments() { return new Type[]{clazz}; } @Override public Type getRawType() { return List.class; } @Override public Type getOwnerType() { return null; } }; }调用时传入
MyClass.class:List<MyClass> o = new Gson().fromJson(tr, getType(MyClass.class));
内容的提问来源于stack exchange,提问作者Sudhanshu Bansal

