首页 > 解决方案 > 如何用 Gson 替换 Guava 中的 TypeToken(特别是在使用泛型类型时)

问题描述

我目前正在尝试重构一个项目以避免不得不使用番石榴作为依赖项。我主要将它用于反射相关的功能,但在大多数情况下,我可以找到一个替代方案。

但是,有一种情况我暂时无法解决:我尝试检索当前类的泛型参数的类。

它可以正常使用Guava,因为返回的类型TokenType可以转换为类。

番石榴:

(Class<I>) new com.google.common.reflect.TypeToken<I>(getClass()) {}.getType();

我试图用TypeTokenfromGson代替这个调用,但显然关于 I 的信息丢失了,并且这个调用的结果是一个TypeVariable比在执行后保留为一个类:

格森:

(Class<I>) new com.google.gson.reflect.TypeToken<I>() {}.getType();

据我所知, getClass() 参数是为什么不保留上下文的线索。当我使用Guava相同的签名时:

(Class<I>) new com.google.gson.reflect.TypeToken<I>() {}.getType();

我收到此错误消息:

java.lang.IllegalStateException:无法为类型变量构造 TypeToken。您可能打算调用 new TypeToken(getClass()) 来为您解析类型变量。如果您确实需要创建类型变量的 TypeToken,请改用 TypeToken.of()。

Gson也将其视为使用Typeas 参数的(包保护的)构造函数。

有没有办法Gson只使用获取通用参数的类?

编辑#1:

更好地理解这个问题我可以说差异似乎位于两种方法返回的底层 Type 实现中。

Guava 可以铸造,Class<I>而 Gson 则不能。我不确定 Guava 的底层实现是什么,但 Gson 使用的是TypeVariable.

目前我发现的解决方法是通过将 Gson TypeToken (Type) 的结果与使用当前类作为上下文的方法合并来利用 Guava 使用的两个信息 (Type + current class):

public static Type getActualType(Class context, Type parameterizedType) {
    if (parameterizedType instanceof TypeVariableImpl) {
        TypeVariable[] genericParameters = context.getSuperclass().getTypeParameters();
        Type[] implementations = ((ParameterizedTypeImpl) context.getGenericSuperclass()).getActualTypeArguments();
        for (int i = 0; i < genericParameters.length; i++) {
            if (genericParameters[i].getName().equals(((TypeVariableImpl) parameterizedType).getName())) {
                return implementations[i];
            }
        }
    }
    return parameterizedType;
}

(这个实现来自buremba/netty-rest 项目

这允许我通过调用来检索预期的类:

(Class<I>) getActualType(getClass(), new com.google.gson.reflect.TypeToken<I>() {}.getType());

这似乎有点费力,我不确定这是最好的方法。

注意:这个问题也是作为Gson 项目的 Github 问题提出的

标签: javagenericsreflectiongsonguava

解决方案


推荐阅读