Java中字段类型实现的接口通用信息

Posted

技术标签:

【中文标题】Java中字段类型实现的接口通用信息【英文标题】:Generic information about interface implemented by field type in Java 【发布时间】:2021-11-09 09:43:05 【问题描述】:

假设我们有一个类型CustomMap<A, B>,它扩展了CommonMap<C, D>,而Map<E, F>又实现了Map<E, F>

自然会期望A == C == E,但并非总是如此 - 例如,您可以让CustomMap<V> 实现Map<String, V>

问题是,假设我有一个Field,而field.getType() 是实现Map<K, V> 的某个接口或类。类型本身可能是非泛型的,也可能是泛型的,但具有不同于<K, V> 的泛型签名等。我如何使用反射获得MapKV 类型参数?

【问题讨论】:

你不能。 Java 有type erasure,这意味着类型参数在编译代码时会被删除。结果类型是最具体的绑定类型,可能是Object 这不正确,类型信息保留在字段中,您可以通过field.getGenericType()获取。 这是可能的,但是正确处理所有极端情况的代码会很糟糕。例如,字段的类型可以引用声明类的类型变量,其边界引用声明类的外部类的类型变量。当遍历字段类型的超类型层次结构时,可能存在类似的极端情况,以用它们的实际参数替换类型变量。另外,没有公共实现类型或工厂方法来生成对象来表达结果(即在找到实际的XY 之后为Map<X,Y> 构造ParameterizedType)。 这有点麻烦,但在实践中完全可行。我将发布我所学到的要点。 【参考方案1】:

似乎必须编写爬取类型信息的代码,记住哪个类型对应于类型参数名称,然后进行替换。代码可能会根据具体情况而有所不同,例如类/接口等,但其核心是:

    // Contains mappings such as K -> Integer, V -> String
    Map<String, Type> typeParameters = new HashMap<>();
    ParameterizedType pType = (ParameterizedType) type;
    // Actual type arguments of a specific parametrized type, e.g. <Integer, PARAM> - 
    // the latter is resolved against typeParameters map
    Type[] typeArguments = pType.getActualTypeArguments();

    cls = ((Class) ((ParameterizedType) type).getRawType());

    // Named arguments of type as declared, e.g. <K, V>
    TypeVariable<?>[] typeParamDecls = cls.getTypeParameters();

    for (int i = 0; i < Math.min(typeParamDecls.length, typeArguments.length); i++) 
        Type value;
        if (typeArguments[i] instanceof TypeVariable) 
            value = typeParameters.get(((TypeVariable<?>) typeArguments[i]).getName());
         else 
            value = typeArguments[i];
        

        typeParameters.put(typeParamDecls[i].getName(), value);
    

这需要递归地应用于cls.getGenericSuperclass() / cls.getGenericInterfaces(),直到你首先找到你想要的类型。如果在任何地方使用原始类型,可能会出现极端情况。

【讨论】:

类型并不总是ParameterizedType。想想class StringMap implements Map&lt;String,String&gt; 或现实生活中的例子class Properties implements Map&lt;Object,Object&gt;。在这些情况下,类型只是 Class,但 getGenericSuperclass()/ getGenericInterfaces() 仍然可以工作(换句话说,它与原始类型无关)。此外,替换也可以递归地应用于类型参数,想想class StringMap extends MultiMap&lt;String,String&gt; class MultiMap&lt;K,V&gt; implements Map&lt;K,List&lt;V&gt;&gt; (但没有API来创建代表List&lt;String&gt;Type对象)...... 您的代码中没有递归。在我的implements Map&lt;K,List&lt;V&gt;&gt; 示例中,只有一个instanceof TypeVariable 评估为false,因为List&lt;V&gt; 不是类型变量,而是参数化类型(指的是您的代码不能用其实际参数替换的类型变量) . 它无法处理我的示例。点。 我不知道这条评论是什么意思。你在这里问了一个问题,我在this comment 告诉你,处理所有情况的正确解决方案非常复杂。现在,您发布了一个不完整的解决方案,该解决方案在更简单的情况下都失败了,甚至没有接近该评论中提到的极端情况,并且拒绝承认它是不完整的。这对任何人有什么帮助? 我没有问任何问题。我告诉过你,你的问题并没有被这个“答案”回答。

以上是关于Java中字段类型实现的接口通用信息的主要内容,如果未能解决你的问题,请参考以下文章

java获取mysql数据库表字段字段类型字段注释

扩展mybatis和通用mapper,支持mysql的geometry类型字段

java33

Msq 中tinyint字段对应 java中哪个类型?

使用通用 java 接口获取意外参数类型并实现

如何增加自定义的字段