使用反射在运行时推断Java泛型参数

5

我有一个带有以下签名的方法:

// Converts a json string to a list of objects
// Assumption: json is an array, and all items in the list are of the same type
public <T> List<T> getListFromJson( String json, Class<T> itemType, List<T> defValue ) {
    final ArrayList<T> list = new ArrayList<T>();

    for( JsonElement e : parser.parse(json).getAsJsonArray())
        list.add( (T) (
                Number.class.isAssignableFrom(itemType) ? e.getAsNumber() :
                Boolean.class.isAssignableFrom(itemType) ? e.getAsBoolean() :
                Character.class.isAssignableFrom(itemType) ? e.getAsCharacter() :
                String.class.isAssignableFrom(itemType) ? e.getAsString() :
                JsonElement.class.isAssignableFrom(itemType) ? e :
                null
            )
        );

    return list;
}

它读取json字符串并将其转换为适当类型的对象列表,例如Integer,String等。
是否有一种强大的方法可以通过从List 参数中推断出Class 参数来从方法中删除Class 参数?例如,我是否可以更改方法签名为以下内容而不失去功能?
public <T> List<T> getListFromJson( String json, List<T> defValue ) {
    ...
}

看起来解决方案需要对ParameterizedType进行一些高级操作。我查看了以下内容,但是要么我使用这些方法不正确,要么它们没有做到我的期望:

2个回答

8
由于类型擦除,你绝对无法“推断”出T是什么——它甚至在运行时都不存在。你能做到的最接近的方法是检查defValue中的值(如果有值)并获取其中元素的类。
Class<?> tType = defValue.get(0).getClass();

if (Boolean.class.isAssignableFrom(tType)) { //...  

编辑

关于您考虑使用像getTypeArguments()等反射方法的想法。这些方法只提供声明的类型数据,而不是实际的类型数据。例如,如果您获取到一个Method对象的句柄并调用getTypeParameters(),您只会得到一个包含表示T的类型对象数组,而不是在某个特定运行时调用中T所代表的实际类型。


你可能想要检查那里是否为空,但原则是正确的。至于空列表该怎么办——不存在的条目的名义类型是否重要呢?听起来有点哲学... - PaulJWilliams
@Mark Peters 但是像 Jackson 这样的库实际上已经可以做到这点了。如果我有一个类 class Wrapper { List<Wrapped> list;} 和另一个 class Wrapped { int onlyField;},它将会把一个 Wrapper 对象的 JSON 字符串表示反序列化为正确的对象类型。 - Arya Pourtabatabaie
1
@AryaPourtabatabaie:是的,因为这些类型是类成员的声明类型的一部分。这些静态类型(字段类型、方法签名等)被实体化(包含为元数据)到编译后的类文件中,并且在编译和运行时链接(如方法调用)时需要。这些类型对运行时和像Jackson这样的库可用。请注意您的示例与问题中有class Wrapper<T> { List<T> list; }的示例之间的区别。 - Mark Peters

1
我所知道的唯一保证在运行时访问类型的方法是将其作为对象构造函数的参数。
class MyClass<T> {
    private final Class<T> clazz;
    public MyClass(Class<T> clazz) {
        this.clazz=clazz;
    }
}

然后在实例化对象时,您必须传递该类:

MyClass<String> object = new MyClass<String>(String.class);

显然,在你的情况下,你有一个有效的静态实用方法而没有对象,所以我认为你只能使用类参数或某种模板对象。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接