获取抽象超类上泛型类型参数的实际类型

2022-09-03 03:19:23

我有一个类,如:

public abstract class BaseDao<T extends PersistentObject> {

  protected Class<T> getClazz() {
     return T.class;
  }

  // ...

}

但是编译器说: 。T.class;Illegal class literal for the type parameter T

我怎样才能得到的类?T


答案 1

绝对可以从 Class#getGenericSuperclass() 中提取它,因为它不是在运行时定义的,而是在编译时由 .FooDao extends BaseDao<Foo>

下面是一个启动示例,如何在抽象类的构造函数中提取所需的泛型超类型,同时考虑子类的层次结构(以及将其应用于泛型方法而无需显式提供类型的实际用例):EntityManager

public abstract class BaseDao<E extends BaseEntity> {

    @PersistenceContext
    private EntityManager em;

    private Class<E> type;

    @SuppressWarnings("unchecked") // For the cast on Class<E>.
    public BaseDao() {
        Type type = getClass().getGenericSuperclass();

        while (!(type instanceof ParameterizedType) || ((ParameterizedType) type).getRawType() != BaseDao.class) {
            if (type instanceof ParameterizedType) {
                type = ((Class<?>) ((ParameterizedType) type).getRawType()).getGenericSuperclass();
            } else {
                type = ((Class<?>) type).getGenericSuperclass();
            }
        }

        this.type = (Class<E>) ((ParameterizedType) type).getActualTypeArguments()[0];
    }

    public E find(Long id) {
        return em.find(type, id);
    }

    public List<E> list() {
        return em.createQuery(String.format("SELECT e FROM %s e ORDER BY id", type.getSimpleName()), type).getResultList();
    }

    // ...
}

答案 2

实际上,这并不像看起来那么容易。当您具有丰富的类型层次结构并希望在超类型中获取泛型参数时,存在问题。例如,您可能具有以下层次结构:

public abstract class BaseDao<T extends BaseEntity> {
...
}

public abstract class SpecialDao<X extends SomeType, E extends BaseEntity> extends BaseDao<E> {
...
}

public class MyDao extends SpecialDao<TypeImpl, EntityImpl> {
...
}

在 return 的实例中调用,但是当您在方法中调用它时,您不知道泛型层次结构有多深。而且,据我所知,你无法获得超类型的泛型超型。因此,当您调用(为了可读性而省略了一些类型转换)时,您将获得(注意而不是)。由于从类型中去除了所有类型变量映射,因此我们从未映射的类型变量和 .然后只需将这些类型变量映射到它们在 中的位置。getClass().getGenericSuperclass()MyDaoSpecialDao<TypeImpl, EntityImpl>BaseDaogetClass().getGenericSuperclass().getRawType().getGenericSuperclass()BaseDao<E><E><T>getRawType()XEgetGenericSuperclass()BaseDao

可以使用此行为,以便我们在遍历类型层次结构时保持从类型变量到其实际值的映射。当我们命中我们想要的类时,我们只需在映射中查找其类型参数即可。代码如下:

@SuppressWarnings("unchecked")
public static <T> Class<T> getGenericClassParameter(final Class<?> parameterizedSubClass, final Class<?> genericSuperClass, final int pos) {
    // a mapping from type variables to actual values (classes)
    Map<TypeVariable<?>, Class<?>> mapping = new HashMap<>();

    Class<?> klass = parameterizedSubClass;
    while (klass != null) {
        Type type = klass.getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            ParameterizedType parType = (ParameterizedType) type;
            Type rawType = parType.getRawType();
            if (rawType == genericSuperClass) {
                // found
                Type t = parType.getActualTypeArguments()[pos];
                if (t instanceof Class<?>) {
                    return (Class<T>) t;
                } else {
                    return (Class<T>) mapping.get((TypeVariable<?>)t);
                }
            }
            // resolve
            Type[] vars = ((GenericDeclaration)(parType.getRawType())).getTypeParameters();
            Type[] args = parType.getActualTypeArguments();
            for (int i = 0; i < vars.length; i++) {
                if (args[i] instanceof Class<?>) {
                    mapping.put((TypeVariable)vars[i], (Class<?>)args[i]);
                } else {
                    mapping.put((TypeVariable)vars[i], mapping.get((TypeVariable<?>)(args[i])));
                }
            }
            klass = (Class<?>) rawType;
        } else {
            klass = klass.getSuperclass();
        }
    }
    throw new IllegalArgumentException("no generic supertype for " + parameterizedSubClass + " of type " + genericSuperClass);
}

推荐