与父级一起使用的getActualTypeArguments从子级获取其参数化类型

时间:2018-07-17 18:52:28

标签: java generics reflection parameterized-types

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

是否可以从其子级获取参数化类型的超类获取实际类型参数?例如。

GenericSubclass<T> extends GenericClass<T>

通过实例化GenericSubclass为:

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

并执行以下操作将返回“ T”

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

但是我需要的是java.lang.String。 任何评论表示赞赏

1 个答案:

答案 0 :(得分:1)

我只是假设阅读此答案的任何人都已经知道类型擦除。如果您不这样做,则应该遵循Type Erasure教程,并且我还建议您仔细阅读问题解答Java generics type erasure: when and what happens

关于类型擦除的解释提供了一些有用的背景知识,但实际上并没有解释getActualTypeArguments()的作用以及问题中的代码为何不起作用的原因。


对于Class cc.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,其通用超类是ArrayList<Long>现在,您可以获得ArrayList<Long>的实际类型参数,即Long

Here's a short program展示了以上所有内容。)


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

这是一个示例,它允许任意数量的临时超类:

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也会自动执行类似的操作。