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

Posted

技术标签:

【中文标题】获取抽象超类上泛型类型参数的实际类型【英文标题】:Get actual type of generic type argument on abstract superclass 【发布时间】:2013-09-13 11:51:16 【问题描述】:

我有这样的课:

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的类?

【问题讨论】:

Get generic type of java.util.List 的可能重复项 你能提供更多的背景信息吗?为什么需要T这个类? @MartijnCourteaux 我在问我的问题之前已经看到了这些问题,但我没有在那里找到答案。所以我不认为重复。 @arshajii 它是一个 Spring/Hibernate-DAO(又名存储库),Spring 需要实体的类。 (我同意你的观点,有更好的方法,但我必须使用给定的 API。) @arshajii 谢谢。这已经是我目前的解决方法。我想,有更好的方法...... :( 【参考方案1】:

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

这是一个启动示例,您可以如何在抽象类的构造函数中提取所需的泛型超类型,同时考虑到子类的层次结构(以及将其应用于没有需要明确提供类型):

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();
    

    // ...

【讨论】:

"对于类的演员表。" - 您还可以将@SuppressWarnings 缩小为Class&lt;E&gt; 的局部变量。无论如何,+1 谢谢你,BalusC。它运作良好,即使我不完全理解它。 +1【参考方案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> 
...

MyDao 的实例中调用getClass().getGenericSuperclass() 会返回SpecialDao&lt;TypeImpl, EntityImpl&gt;,但是当你在BaseDao 方法中调用它时,你不知道泛型层次结构有多深。此外,据我所知,您无法获得超类型的通用超类型。因此,当您调用getClass().getGenericSuperclass().getRawType().getGenericSuperclass()(为了便于阅读而省略了一些类型转换)时,您将得到BaseDao&lt;E&gt;(注意&lt;E&gt; 而不是&lt;T&gt;)。由于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);

【讨论】:

【参考方案3】:

如果 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】:

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

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(请参阅底部的输出)

为我糟糕的英语干杯和抱歉

【讨论】:

这在两台机器中的一台上工作,第二台机器抱怨sun.reflect.generics.reflectiveObjects.TypeVariableImpl 无法转换为Class。有没有办法让这个更可靠? See here 我正在使用它的上下文。 您看到的错误是因为运行时代理了您的晚餐类,而您并没有真正获得超类而是代理类,您可以通过添加System.out.println(type.getClass().getName())来检查这一点,并验证是否该类是正确的,如果不是,请执行if (type.getClass().getName().startsWith('$')) type .= type.getGenericSuperclass();之类的操作【参考方案5】:

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

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

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

  protected Class<T> getClazz() 
     return classT;
  

  // ...


【讨论】:

【参考方案6】:

您可以查看TypeTools 了解此内容:

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

【讨论】:

以上是关于获取抽象超类上泛型类型参数的实际类型的主要内容,如果未能解决你的问题,请参考以下文章

java筑基.泛型,反射,注解-利用注解加反射练习

Java泛型

java 如何获得List的泛型类型

如何获取java泛型的参数类型

java获取泛型class

TypeScript - 获取泛型类参数的类型