【问题标题】:How does Gson TypeToken work?Gson TypeToken 是如何工作的?
【发布时间】:2015-05-02 17:06:58
【问题描述】:

我知道在 Java 中与 C# 泛型相反,例如,C# 泛型是编译时特性,并且通过类型擦除被删除。那么,Gson 的TypeToken 究竟是如何工作的呢?它如何获取对象的泛型类型?

【问题讨论】:

    标签: java generics gson erasure


    【解决方案1】:

    From §4.6 of the JLS(强调我的):

    类型擦除是从类型(可能包括参数化类型和类型变量)到类型(绝不是参数化类型或类型变量)的映射。我们写 |T|用于类型 T 的擦除。擦除映射定义如下:

    参数化类型(第 4.5 节)G 的擦除是 |G|。

    嵌套类型 T.C 的擦除是 |T|.C.

    数组类型T[]的擦除是|T|[]。

    类型变量的擦除(第 4.4 节)是其最左边界的擦除。

    所有其他类型的擦除都是类型本身。

    因此,如果您声明一个具有其自身匿名子类的类,它会保持其参数化类型;它没有被删除。因此,请考虑以下代码:

    import java.lang.reflect.ParameterizedType;
    import java.util.Arrays;
    import java.util.HashMap;
    
    public class Erasure<T>
    {
        public static void main(String...strings) {
          Class<?> foo = new Erasure<HashMap<Integer, String>>() {}.getClass();
          ParameterizedType t = (ParameterizedType) foo.getGenericSuperclass();
          System.out.println(t.getOwnerType());
          System.out.println(t.getRawType());
          System.out.println(Arrays.toString(t.getActualTypeArguments()));
        }
    }
    

    这个输出:

    null
    class Erasure
    [java.util.HashMap<java.lang.Integer, java.lang.String>]
    

    请注意,如果你没有匿名声明类,你会得到一个ClassCastException,因为擦除;超类不是参数化类型,而是Object

    【讨论】:

    • 你确定“所有其他类型的擦除就是类型本身。”正在防止匿名类的类型擦除? JLS 中的那句话意味着擦除,例如,String 是 String。匿名类只是获取直接超类的一种便捷方式,getGenericSuperclass 可以从中获取类型信息
    • @Ivan 这也意味着TypeReference&lt;Foo&gt;() {}的擦除是TypeReference&lt;Foo&gt;() {}。这意味着两个
    • 但是 getGenericSuperclass 不仅适用于匿名类,也适用于普通扩展。那么擦除也不适用于超标吗?我有点困惑,所以我的问题也可能令人困惑
    • @Ivan 也许你应该试试看会发生什么。 class MyAwesomeList&lt;T&gt; extends java.util.ArrayList&lt;T&gt; { }new MyAwesomeList&lt;String&gt;().getClass().getGenericSuperClass()
    • 可能我明白了你的意思:getGenericSuperclass 显示了类“extends”语句中提供的实际类型参数,因此我们将在您的示例中得到 T。匿名类隐式扩展了超类,所以我们可以在其实例上获取实际类型。但我还是不同意The erasure of every other type is the type itself.在这里发挥作用
    【解决方案2】:

    Java 的类型擦除适用于单个对象,而不是类、字段或方法。 TypeToken 使用匿名类来确保它保留泛型类型信息,而不仅仅是创建一个对象。

    【讨论】:

      【解决方案3】:

      Gson TypeToken 使用 Neal Gafter 的 super type tokens 模式。此模式基于 docs 中的 Class#getGenericSuperclass() 方法

      返回代表实体直接超类的Type (类、接口、原始类型或 void)由该类表示。 如果超类是参数化类型,则返回Type 对象 必须准确反映源中使用的实际类型参数 代码。

      这实质上意味着如果你有一个扩展参数化类的类,那么你可以获得超类的实际类型参数

      ((ParameterizedType)myClassObj.getGenericSuperClass()).getActualTypeArguments()
      

      所以当你在 Gson 中创建类型标记时

      Type someTypeToken = new TypeToken<Collection<Integer>>(){};
      
      // This essentially is same as defining
      class AnonClass extends TypeToken<Collection<Integer>>{}
      

      现在您可以轻松地将类型参数获取到上面指定的超类型(TypeToken)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-08-05
        • 1970-01-01
        • 1970-01-01
        • 2014-01-13
        相关资源
        最近更新 更多