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.inder.LinkedTreeMap强制转换为类org.example.Main$MyClass(com.google.gson.inder.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类的一个实例.

作为附注,您的代码包含两个非类型安全操作:

  • Creating a TypeToken<List<T>>
    As you mention at runtime due to type erasure Gson cannot know the actual type of T. So if your example had a MySubclass extends MyClass, then even if Gson's type erasure was correct you could still see a ClassCastException when you call <MySubclass>getType(). Because at runtime this information is not available so it would actually be a TypeToken<List<T extends MyClass>>, erased to TypeToken<List<MyClass>> instead of TypeToken<List<MySubclass>>.
    Due to that future Gson versions will disallow capturing type variables by default.

  • Using Gson.fromJson(..., Type)
    These methods cannot enforce type-safety and allow you to write something like the following without causing any compilation warning or error:

    List<MyClass> l = gson.fromJson(json, new TypeToken<List<WrongClass>>() {}.getType());
    

    (请注意不匹配的MyClassWrongClass)

    您应该更喜欢类型安全的Gson.fromJson(..., TypeToken<T>)重载,这是在gson 2.10中添加的.也就是说,只需省略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中,如果返回类型为make the type variable reified并将返回类型从Type更改为TypeToken<List<T>>,则原始示例也可以使用.

Java相关问答推荐

使用Apache Poi MQLSlideShow,在XSLFTable表中,我们可以在文本段落后面的每个单元格中包含圆角矩形吗?

将linkedHashMap扩展到Java中的POJO类

如何将kotlin代码转换为java

Spring boot:Bean和动态扩展器

Android Studio—java—在onBindViewHolder中,将断点和空白添加到BackclerView中

在spring—data中自动发现native—sql查询期间遇到重复的SQL别名[id]

如何从错误通道回复网关,使其不会挂起

如何使用SpringBoot中的可分页对整数作为字符串存储在数据库中时进行排序

用户填充的数组列表永不结束循环

在Eclipse中数组的可空性

如何在Cosmos DB(Java SDK)中增加默认响应大小

将ByteBuffer异步写入InputStream或Channel或类似对象

Java在操作多个属性和锁定锁对象时使用同步和易失性

如何在右击时 Select 新行?

在JSON上获取反斜杠

java.exe如何执行java源代码?

Spring Integration SFTP 连接失败 - 无法协商 kex 算法的密钥交换

为什么 log4j 过滤器在appender中不起作用

人们在 IntelliJ 上将-Dhttp.proxyHost=your.proxy.net -Dhttp.proxyPort=8080放在哪里?

Xml Reader 将 BMP 外部的字符解析为代理项对,这会导致无效的 xml