【问题标题】:Inferring java generics parameters at runtime using reflection使用反射在运行时推断 java 泛型参数
【发布时间】:2010-12-09 23:22:17
【问题描述】:

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

// 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 字符串并将其转换为适当类型对象的列表,例如。整数、字符串等

是否有一种可靠的方法可以通过从 List&lt;T&gt; 参数推断它来从方法中删除 Class&lt;T&gt; 参数?例如。有没有办法可以在不丢失功能的情况下将方法签名更改为以下内容?

public <T> List<T> getListFromJson( String json, List<T> defValue ) {
    ...
}

似乎解决方案需要对 ParameterizedType 进行一些花哨的操作。我查看了以下内容,但要么是我错误地使用了这些方法,要么它们没有按照我的预期进行:

【问题讨论】:

    标签: java generics reflection


    【解决方案1】:

    由于type erasure,您绝对无法“推断”T 是什么——它甚至在运行时都不存在。您最接近的方法是检查 defValue 中的值(如果它有值)并在那里获取元素的类。

    Class<?> tType = defValue.get(0).getClass();
    
    if (Boolean.class.isAssignableFrom(tType)) { //...  
    

    编辑

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

    【讨论】:

    • 您可能想在那里检查是否为空,但原则是合理的。至于你用一个空的 List 做什么——好吧,不存在的条目的名义类型是什么有关系吗?听起来很哲学……
    • @Mark Peters 但是像杰克逊这样的图书馆实际上是这样做的。如果我有一个 class Wrapper { List&lt;Wrapped&gt; list;} 和另一个 class Wrapped { int onlyField;} 类,它会将 Wrapper 对象的 JSON 字符串表示反序列化为正确的对象类型。
    • @AryaPourtabatabaie:是的,因为这些类型是类成员的 declared 类型的一部分。这些静态类型(字段类型、方法签名等)被reified(作为元数据包含)到已编译的类文件中,并且是编译和运行时链接所必需的,例如方法调用。这些类型可供运行时使用,Jackson 等库使用它。请注意您的示例与问题中的 class Wrapper&lt;T&gt; { List&lt;T&gt; list; } 示例之间的区别。
    【解决方案2】:

    我知道保证在运行时访问该类型的唯一方法是将其作为对象构造函数的参数:

    class MyClass<T> {
        private final Class<T> clazz;
        public MyClass(Class<T> clazz) {
            this.clazz=clazz;
        }
    }
    

    然后你必须在实例化对象时传递类:

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

    显然,在您的情况下,您实际上是一个静态实用程序方法并且没有对象,所以我认为您要么使用 Class 参数,要么使用某种模板对象。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-02-26
      • 1970-01-01
      • 2012-06-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多