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 + '\'' +
'}';
}
}
我试图了解 Gson 如何与泛型一起工作。 我在上面的主要方法中遇到错误。
线程“main”中的异常 java.lang.ClassCastException:类 com.google.gson.internal.LinkedTreeMap 无法转换为类 org.example.Main$MyClass(com.google.gson.internal.LinkedTreeMap 和 org.example. Main$MyClass 位于加载程序“app”的未命名模块中)
由于类型擦除,不应在 getType() 方法中将 T 设置为 MyClass。那么,Gson 应该创建一个
List<MyClass>
的实例而不是 List<Object>
吗?为什么 T 在这里被视为对象而不是 MyClass?
我尝试运行此代码,但它给了我错误。
你是对的,这种行为与正常的 Java 类型擦除不匹配。然而,Gson 实际上看到了类型变量
T
,然后自己对其执行(不正确的)类型擦除。 Gson 目前忽略类型变量的边界,并始终使用 Object
代替,请参阅 issue 2563。
这会导致您看到的异常:Gson 将值反序列化为
Object
,并且因为它是一个 JSON 对象,Gson 创建一个 Map
,更具体地说是其内部 LinkedTreeMap
类的实例。
作为旁注,您的代码包含两个非类型安全操作:
创建一个
TypeToken<List<T>>
T
的实际类型。因此,如果您的示例有一个 MySubclass extends MyClass
,那么即使 Gson 的类型擦除是正确的,当您调用 ClassCastException
时,您仍然可以看到 <MySubclass>getType()
。因为在运行时此信息不可用,所以它实际上是一个 TypeToken<List<T extends MyClass>>
,被删除为 TypeToken<List<MyClass>>
而不是 TypeToken<List<MySubclass>>
。使用
Gson.fromJson(..., Type)
List<MyClass> l = gson.fromJson(json, new TypeToken<List<WrongClass>>() {}.getType());
(注意
MyClass
与 WrongClass
不匹配)
您应该更喜欢 Gson 2.10 中添加的类型安全
Gson.fromJson(..., TypeToken<T>)
重载。也就是说,只需省略 TypeToken.getType()
调用即可。
getType()
方法的类型安全变体可能如下所示:
private static <T extends MyClass> TypeToken<List<T>> getType(Class<T> elementClass) {
return (TypeToken<List<T>>) TypeToken.getParameterized(List.class, elementClass);
}
在 Kotlin 中,如果您 创建类型变量
reified
并将返回类型从 Type
更改为 TypeToken<List<T>>
,则原始示例也可以工作。