【问题标题】:Create TypeToken for type argument of generic super type为泛型超类型的类型参数创建 TypeToken
【发布时间】:2016-08-25 09:53:11
【问题描述】:

我正在为两个依赖泛型模型实现 Gson TypeAdapter

interface ModelA<T> {}
interface ModelB<T> extends ModelA<List<T>> {}

为此,我需要获取TypeTokenTypeAdapter。通过做

Type type = ((ParameterizedType) type).getActualTypeArguments()[0];
TypeToken<?> token = TypeToken.get(type);
TypeAdapter<?> adapter = gson.getAdapter(token);

我得到了任何给定类型AnyType 和相关适配器的类型标记。这是我对ModelA 所需要的,但对于ModelB,我需要List&lt;AnyType&gt; 的适配器,这是直接使用ModelA&lt;List&lt;AnyType&gt;&gt; 的结果。

使用原始类型令牌传递的原始类型的接口

token.getRawType().getGenericInterfaces()[0]

我似乎只得到List的类型擦除类型。

如何结合这两个信息来获得通用的List&lt;AnyType&gt; 类型或直接创建它?作为输入,它只有ModelB&lt;AnyType&gt; 的类型标记。

示例

对于给定的行为,我实施了一个简单的测试来显示差异。我使用 Gson 内部非 API 类 $Gson$Types,从 hahn 的答案中提取了 expected 部分。

public class TypeTokenValidate {

    public interface ModelA<T> {}
    public interface ModelB<T> extends ModelA<List<T>> {}

    @Test
    public void genericToken() throws Exception {
        TypeToken<ModelB<String>> token = new TypeToken<ModelB<String>>() {};
        ModelB<String> m = new ModelB<String>() {};
        Type expected = $Gson$Types.resolve(ModelB.class, m.getClass(),
                ModelA.class.getTypeParameters()[0]);
        assertEquals(expected, getActual(token));
    }

    private static Type getActual(TypeToken<?> token) {
        Type type = token.getType();
        //type = token.getRawType().getGenericInterfaces()[0];
        type = ((ParameterizedType) type).getActualTypeArguments()[0];
        return TypeToken.get(type).getType();
    }
}

【问题讨论】:

  • 您的意思是token.getRawType().getGenericInterfaces()[0] 不能转换为ParametrizedType?如果是,您只需递归地继续。
  • 是的,但getActualTypeArguments()[0] 似乎没有泛型类型信息。因此我只得到List,没有任何关于T的信息
  • 我们知道java.util.List 有一个类型参数,所以如果你直接使用反射到 List.class 那么信息应该在那里,对吧?
  • 反射不提供任何关于泛型的信息。我认为这是原始类型的问题,因为它是 Class&lt;?&gt; 类型,因此会在运行时擦除泛型类型。
  • 对于 ModelB 或 ModelA,你会得到什么合适的 ````type```?那不是 ModelB.class.getTypeParameters() 吗?您可以将其添加到您的问题代码中吗? List.class.getTypeParameters() 给你什么?

标签: java android generics gson typetoken


【解决方案1】:

有一个$Gson$Types,您可以使用它来检索键入的ListType。例如有:

ModelB<String> m = new ModelB<String>() {};
Type resolve = $Gson$Types.resolve(m.getClass(), m.getClass(), ModelA.class.getTypeParameters()[0]);

将返回 Type 并将 rawType 设置为 ListtypeArguments[String]。从而根据需要保留类型。

然后执行这个块

TypeToken<?> token = TypeToken.get(resolve);
TypeAdapter<?> adapter = gson.getAdapter(token);

将导致CollectionTypeAdaptorFactory 的适配器将elementTypeAdapter.type 设置为String 类。

【讨论】:

  • 这似乎工作正常,但有没有办法不直接使用内部类$Gson$Types 来做到这一点?
【解决方案2】:

如果您使用.getType() 而不是.getRawType(),您正在寻找的信息似乎会被保留:

import java.util.List;
import java.lang.reflect.ParameterizedType;
import com.google.gson.reflect.TypeToken;

public class Test  {

   public interface ModelA<T> {};

   public interface ModelB<T> extends ModelA<List<T>> {}

   public static void main(String[] args) {

        // Using java.lang.reflect.*:

        System.out.println(ModelB.class.getGenericInterfaces()[0]);
        // out: Test.Test$ModelA<java.util.List<T>>
        System.out.println(((ParameterizedType)ModelB.class.getGenericInterfaces()[0]).getActualTypeArguments()[0]);
        // out: java.util.List<T>
        System.out.println(((ParameterizedType)((ParameterizedType)ModelB.class.getGenericInterfaces()[0]).getActualTypeArguments()[0]).getActualTypeArguments()[0]);
        // out: T

        // Recovery from a TokenType instance...

        // ... using .getRawType()
        System.out.println(TypeToken.get(ModelB.class.getGenericInterfaces()[0]).getRawType());
        // out: interface Test$ModelA
        System.out.println(TypeToken.get(((ParameterizedType)((ParameterizedType)ModelB.class.getGenericInterfaces()[0]).getActualTypeArguments()[0])).getRawType());
        // out: interface java.util.List

        // ... using .getType()
        System.out.println(TypeToken.get(ModelB.class.getGenericInterfaces()[0]).getType());
        // out: Test$ModelA<java.util.List<T>>
        System.out.println(TypeToken.get(((ParameterizedType)((ParameterizedType)ModelB.class.getGenericInterfaces()[0]).getActualTypeArguments()[0])).getType());
        // out: java.util.List<T>
   }
}

希望你能弄清楚如何从那里创建你的TokenAdaptors

【讨论】:

  • 如何使用非通用的T (如String)来做到这一点?这仅在toString() 的实现上有所不同。我更新了我的问题,因为它并不完全清楚。
  • @tynn 如果您尝试恢复的 部分 只是 出现在变量声明或其他一些非类创建表达式(如构造调用)中。 . 该信息在运行时丢失;您不能期望从任何 X.class 实例或任何实例中提取它。哈恩的解决方案之所以有效,是因为他在声明中声明了一个带有 的内部类,因此他可以挖掘信息。取决于您的情况,这可能对您来说已经足够了,但这样的解决方案将无法处理在运行时构建的任意模型,而无需尾随“{}”。
  • @tynn 除了我提出的解决方案也适用于内部类;我没有测试它,我不明白为什么它不起作用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多