繁体   English   中英

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

[英]Get actual type of generic type argument on abstract superclass

我有一个 class 像:

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

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

    // ...
}

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

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

如果 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);
    }

}

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

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

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

如果您的 dao 层次结构很大,则可以尝试递归查找 BaseDao 并获取参数化类型

请参阅此处的示例(请参阅底部的输出)

为我的英语不好而干杯和抱歉

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

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

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

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

  // ...

}

您可以为此查看TypeTools

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

您可以通过抽象 class 的构造函数传递 class 的类型,如下所示。

public abstract class BaseDao<T extends PersistentObject> {

    private final Class<T> typeClass;

    //Here in the constructor, you are asking for the actual Type Class.
    BaseDao(Class<T> typeClass) {
        this.typeClass = typeClass;
    }

   // ...

}

在具体的 class 中,在构造函数中传递 class 类型。

public class BaseDaoImpl extends BaseDao<ActualType> {
   
   BaseDaoImpl() {
       //Here in the implementation, you pass the type class
       super(ActualType.class);
   }
}

这种方法的问题在于,您无法确保您通过构造函数传递的类型是在泛型中设置的实际类型,因此如果开发人员不确保他们通过构造函数传递正确的类型,这可能会导致问题.

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM