当前位置: 首页 > 知识库问答 >
问题:

运行时的泛型类类型参数详细信息

南门棋
2023-03-14

如果我们提供足够的泛型信息,像Jackson这样的库可以从JSON创建对象。

在杰克逊,我们可以

    Class<?>[] parameterTypes; 
    JavaType type = objectMapper.getTypeFactory().constructParametricType(ObjectClass, parameterTypes);
    objectMapper.readValue(json, type);
  public class MultiLevelGenericTestData<T, V> {
    private GenericTestData<T> tGenericTestData;
    private GenericTestData<V> vGenericTestData;
  }

  public class MultiGenericTestData<K, V> {
    private K key;
    private V value;
  }
 
  public class MultiGenericTestDataSameType<T> {
    private GenericTestData<T> genericTestData;
    private MultiGenericTestData<T, T> multiGenericTestData;
  }
 // This method should find all type's class names in the list
 // that can be used to construct the object without any issue.
void genericFieldClassNames(List<String> types, List<String> classes, Object payload)
      throws IllegalAccessException {
    for (Field field : payload.getClass().getDeclaredFields()) {
      // ignorefield without annotation
      if (!field.isAnnotationPresent(GenericField.class)) {
        continue;
      }
      Type genericType = field.getGenericType();
      // not a generic field
      if (genericType.equals(field.getType())) {
        continue;
      }
      // null value nothing can be done
      Object fieldVal = FieldUtils.readField(field, payload, true);
      if (fieldVal == null) {
        continue;
      }
      String genericFieldType = genericType.getTypeName();
      Class<?> fieldClass = fieldVal.getClass();
      // problematic cases when we start traversing up 
      if (genericFieldType.endsWith(">")) {
        genericFieldClassNames(types, classes, fieldVal);
      } else {
        // here a check can be added to avoid duplicate type name but as soon as  
        // we add type genericFieldType check it will fail when we have used similar  
        // types in construction like MultiGenericTestData<String, String>
        types.add(genericFieldType);
        classes.add(fieldClass.getName());
      }
    }
  }
MultiLevelGenericTestData<String, String> data;
MultiLevelGenericTestData<String, Integer> data;

在这种情况下,我们应该得到[String,Integer]

MultiGenericTestData<String, String> data;

在这种情况下,我们应该得到[String,String]

MultiGenericTestDataSameType<String> data;

在这种情况下,我们应该得到[String]

MultiGenericTestDataSameType< MultiGenericTestData< String, Integer> > data;

为了进一步澄清,我希望在不创建任何其他类的情况下获取类型信息,并且这些信息应该传递给序列化程序,即我不希望更改类似于[]字节序列化(对象有效负载)的序列化程序方法签名。我们可以创建任意多的helper类,也可以强制从某个超类扩展有效负载类(超类可以有逻辑来提取通用信息)。

共有1个答案

章飞章
2023-03-14

这是一个相当长的答案,但应该让你进入一个很好的起点去做你想做的事情。

在运行时获取泛型类型的“技巧”相当古老,使用的最著名的(我猜)现代库是GSONGuava。我想Jackson使用了同样的技巧,因为没有其他方法可以做到这一点。

简单地说,您需要这样一个类:

static abstract class MappingRegistrar<IN> {

    private final Type type;

    protected MappingRegistrar() {
        // more will be here shortly
    }
 
    // ... more will come here shortly

} 
MappingRegistrar<String> one = new MappingRegistrar<>() {};
static abstract class MappingRegistrar<IN> {

    private final Type type;

    protected MappingRegistrar() {
        Class<?> cls = getClass();
        Type[] type = ((ParameterizedType) cls.getGenericSuperclass()).getActualTypeArguments();
        this.type = type[0];
    }

}
static abstract class MappingRegistrar<IN> {

    private final Type type;

    protected MappingRegistrar() {
        Class<?> cls = getClass();
        Type[] type = ((ParameterizedType) cls.getGenericSuperclass()).getActualTypeArguments();
        this.type = type[0];
    }

    public void seeIt() {
        innerSeeIt(type);
    }

    private void innerSeeIt(Type type) {
        if (type instanceof Class) {
            Class<?> cls = (Class<?>) type;
            boolean isArray = cls.isArray();
            if (isArray) {
                System.out.print(cls.getComponentType().getSimpleName() + "[]");
                return;
            }
            System.out.print(cls.getSimpleName());

        }

        if (type instanceof TypeVariable) {
            Type[] bounds = ((TypeVariable<?>) type).getBounds();
            String s = Arrays.stream(bounds).map(Type::getTypeName).collect(Collectors.joining(", ", "[", "]"));
            System.out.print(s);
        }

        if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            String rawType = parameterizedType.getRawType().getTypeName();
            System.out.print(rawType + "<");
            Type[] arguments = parameterizedType.getActualTypeArguments();

            for (int i = 0; i < arguments.length; ++i) {
                innerSeeIt(arguments[i]);
                if (i != arguments.length - 1) {
                    System.out.print(", ");
                }

            }

            System.out.print(">");
            //System.out.println(Arrays.toString(arguments));
        }

        if (type instanceof GenericArrayType) {
            // you need to handle this one too
        }

        if (type instanceof WildcardType) {
            // you need to handle this one too, but it isn't trivial
        }
    }

}
 public class Playground2<R extends Number & Serializable> {

    public static void main(String[] args) {
        new Playground2<Integer>().samples();
    }


    public void samples() {

        MappingRegistrar<String> one = new MappingRegistrar<>() {};
        one.seeIt();
        System.out.println("\n-------------");

        MappingRegistrar<String[]> two = new MappingRegistrar<>() {};
        two.seeIt();
        System.out.println("\n-------------");

        MappingRegistrar<R> three = new MappingRegistrar<>() {};
        three.seeIt();
        System.out.println("\n-------------");

        MappingRegistrar<MultiLevelGenericTestData<String, String>> four = new MappingRegistrar<>() {};
        four.seeIt();
        System.out.println("\n-------------");

        MappingRegistrar<MultiGenericTestDataSameType<MultiGenericTestData<String, Integer>>> five = new MappingRegistrar<>() {};
        five.seeIt();
        System.out.println("\n-------------");

    }
}

结果是:

String
-------------
String[]
-------------
[java.lang.Number, java.io.Serializable]
-------------
Playground2$MultiLevelGenericTestData<String, String>
-------------
Playground2$MultiGenericTestDataSameType<Playground2$MultiGenericTestData<String, Integer>>
-------------
 类似资料:
  • 问题内容: 我有一个方法以a 作为参数。 在中,我如何知道a 是还是a 是? 问题答案: 根据用户omain的回答“如果使用<?>,则意味着您将不会在任何地方使用参数化类型。要么转到特定类型(在您的情况下,似乎是),要么转到非常通用的“ 另外,我相信如果您使用问号,编译器将在运行时(类型;有效Java的第119页)消除类型不匹配的情况,绕过擦除,并有效地消除了使用泛型类型所带来的好处? 要回答发问

  • System.Linq.Enumerable.Cast<T> 强制序列的每个对象转化为目标类型 T 。这是框架的一部分,所以 LINQ 查询中的 IEnumerable (而不是 IEnumerable<T> )才能使用。 Cast<T> 是一个没有约束的泛型方法。这就是限制类型转换使用它。如果你理解 Cast<T> 的这个限制,你会发现你自己想的却不能工作。现实中,它就是该本来那样工作,而不是你

  • 我在我的一个实用程序类中有一个方法,它接受一个集合和一个类对象,并返回一个Iterable实例,该实例可以遍历作为指定类实例的集合的所有成员。其签名为: 这对于大多数用例都非常有效,但现在我需要将其与泛型类

  • 我想使用泛型类作为另一个泛型类的类型参数。 起初,我对类的定义是这样的: 然后我的需求发生了变化,我不得不为我的R类型使用包装器/持有者类 到目前为止,我的尝试:(给出编译时错误:

  • 问题内容: 我刚刚了解了这种美观的语法 用可能是类型的元素来清空。Java的源代码如下所示: 现在,如果我以这种方式编写了一个泛型类型不出现在参数列表中的方法,那么有什么方法可以访问成为的实际类呢? 我的意思是,到目前为止,我编码同一件事的方法是 如果删除了-parameter,我将无法执行。显然我可以 但这给了我通常的警告。好的,在这里有帮助,但是实际上我想对方法的预期返回类型做些事情。如果我添

  • 我试图创建一个Java方法,它接受一个对象类型和它应该转换成的数据类型。 例如,如果我应该能够返回一个值1作为Int或双根据需要。我使用类传递数据类型作为参数。 问题:如何使方法泛型以接受基于输入参数的返回类型? 下面的代码只是一个示例,它可能在语法上不正确,用于解释我的问题。