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

时间:2013-09-09 21:49:22

标签: java generics jpa dao

我有一个类:

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的课程?

6 个答案:

答案 0 :(得分:22)

绝对有可能从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();
    }

    // ...
}

答案 1 :(得分:12)

实际上,这并不像看起来那么容易。如果您具有丰富的类型层次结构并且想要在超类型中获取泛型参数,则会出现问题。例如,您可能具有以下层次结构:

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> {
...
}

getClass().getGenericSuperclass()的实例中调用MyDao会返回SpecialDao<TypeImpl, EntityImpl>,但是当您在BaseDao方法中调用它时,您不知道通用层次结构有多深。而且,据我所知,你无法获得超类型的通用超类型。因此,当您调用getClass().getGenericSuperclass().getRawType().getGenericSuperclass()(为了便于阅读而省略某些类型转换)时,您将获得BaseDao<E>(注意<E>而不是<T>)。由于getRawType()从类型中删除了所有类型变量映射,因此我们从未映射的类型变量XE开始。然后,getGenericSuperclass()只会将这些类型变量映射到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);
}

答案 2 :(得分:7)

如果你的课程是抽象的,你可以试试这个:

public class<T> getClassOfT() {
    final ParameterizedType type = (ParameterizedType) this.getClass()
            .getGenericSuperclass();
    Class<T> clazz = (Class<T>) type.getActualTypeArguments()[0];
    return clazz;
}

仅当实例是直接子类时才有效,并且您想要的类的类型是第一个(参见[0])。

如果你有一个很大的dao层次结构,你可以尝试递归地填充BaseDao并获得参数化类型

查看示例here(请参阅底部的输出)

为我糟糕的英语欢呼和抱歉

答案 3 :(得分:6)

如果Spring框架可用,您可以这样做:

import org.springframework.core.GenericTypeResolver;

public abstract class BaseDao<T extends PersistentObject> {

    protected Class<T> getClazz() {
        return (Class<T>) GenericTypeResolver.resolveTypeArgument(getClass(), BaseDao.class);
    }

}

答案 4 :(得分:1)

以安全的方式对此问题进行排序的常用方法是添加构造函数来存储类型的类。 您的上下文中的示例:

public abstract class BaseDao<T extends PersistentObject> {
  private Class<T> classT;

  BaseDao(Class<T> classT){
    this.classT=classT;
  }

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

  // ...

}

答案 5 :(得分:1)

您可以查看TypeTools

Class<T> t = (Class<T>)TypeResolver.resolveRawArgument(BaseDao.class, getClass());