java - getActualTypeArguments 与父级,从子级获取其参数化类型

标签 java generics reflection parameterized-types

我在发布之前做了一些研究,但没有具体答案。

是否有可能获得父类(super class)的实际类型参数,该父类(super class)从其子类获取其参数化类型?例如..

GenericSubclass<T> extends GenericClass<T>

通过将 GenericSubclass 实例化为:

GenericSubclass<String> genericClass = new GenericSubclass<>();

执行下面的命令会返回“T”

Type type = ((ParameterizedType)this.getClass().
           getGenericSuperclass()).getActualTypeArguments()[0];

但是我需要的是java.lang.String。 任何意见都表示赞赏

最佳答案

我只是假设阅读此答案的任何人都已经了解类型删除。如果不这样做,您应该遵循 Type Erasure教程,我还建议阅读 Q&A Java generics type erasure: when and what happens .

类型删除的解释提供了一些有用的背景,但它实际上并没有解释 getActualTypeArguments() 的含义。确实如此,以及为什么问题中的代码不起作用。

<小时/>

对于Class c , c.getGenericSuperclass()有效地获取c的extends子句中的类型.

例如,如果我们有这个(使用真实的类):

public class ArrayList<E> extends AbstractList<E> {...}

我们说ArrayList.class.getGenericSuperclass() ,我们得到的是一个参数化类型,表示 AbstractList<E> 。同样,这是 extends 子句中的类型。

如果我们有这个:

ArrayList<Long> al = new ArrayList<Long>();

al.getClass()返回ArrayList.class ,所以al.getClass().getGenericSuperclass()再次返回AbstractList<E> .

AbstractList<E> 的“实际”类型参数是类型变量 E ,所以如果我们说:

Type actualTypeArgument =
    ((ParameterizedType)
        al.getClass()
          .getGenericSuperclass())
          .getActualTypeArguments() [0];

然后actualTypeArgument引用类型变量E ,由 ArrayList 声明。 (事实上​​,actualTypeArgument == ArrayList.class.getTypeParameters()[0] 确实如此。)

<小时/>

使用此习惯用法时,通常会在每次创建要捕获其类型参数的类型的实例时创建一个匿名子类:

ArrayList<Long> al = new ArrayList<Long>() {};
//                                    Note ^^

隐式声明一个类,如下所示:

class AnonymousArrayList1 extends ArrayList<Long> {}

(除了匿名类没有名称。此外,我们可以显式声明一个类似的子类,而不是使用匿名类。)

现在当我们调用al.getClass()时我们得到AnonymousArrayList1.class其通用父类(super class)是 ArrayList<Long>现在您可以获取 ArrayList<Long> 的实际类型参数,即Long .

( Here's a short program 演示了上述所有内容。)

<小时/>

至于允许一定程度的间接,这是可能的,但代码更复杂。您还仍然受到上述规则的约束,即必须在 extends 子句中提供实际的类作为类型参数。

下面是一个允许任意数量的临时父类(super class)的示例:

package mcve;

import java.lang.reflect.*;

public abstract class KnowMyType<T> {
    public KnowMyType() {
        System.out.println(getMyType());
    }

    @SuppressWarnings("unchecked")
    private Class<T> getMyType() {
        try {
            return (Class<T>) findMyType(getClass(), null);
        } catch (RuntimeException x) {
            throw new IllegalArgumentException("illegal type argument", x);
        }
    }

    private Type findMyType(Class<?> plainClass, Type genericClass) {
        Class<?> plainSuper = plainClass.getSuperclass();
        Type genericSuper = plainClass.getGenericSuperclass();

        Type t;

        if (plainSuper == KnowMyType.class) {
            t = ((ParameterizedType) genericSuper).getActualTypeArguments()[0];
        } else {
            t = findMyType(plainSuper, genericSuper);
        }

        if (t instanceof TypeVariable<?>) {
            TypeVariable<?>[] vars = plainClass.getTypeParameters();

            for (int i = 0; i < vars.length; ++i) {
                if (t == vars[i]) {
                    t = ((ParameterizedType) genericClass).getActualTypeArguments()[i];
                    break;
                }
            }
        }

        return t;
    }
}

这将让你做到这一点:

class KnowMyTypeSub<T> extends KnowMyType<T> {}
new KnowMyTypeSub<Long>() {}; // constructor prints 'class java.lang.Long'

Guava's TypeToken 也会自动执行类似的操作。

关于java - getActualTypeArguments 与父级,从子级获取其参数化类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51388446/

相关文章:

java - 为什么这个通用方法签名无效?

java - EJB3 Mappings/onetomany/manytoone 如何处理关系

java - 使用 PDFBox 将西里尔字符写入 PDF 表单字段

java - 使用 JBOSS 和 java 以编程方式创建子域

java - 如何访问嵌套在另一个泛型中的泛型?

java - 在Java中动态加载一个类并调用一个方法

java - Android游戏菜单可点击

C# 具有泛型类型的泛型对象实例化

java - 有没有直接的方法来计算原始字段的大小?

C++/Qt TestLib 以编程方式获取类中的函数数