简体   繁体   English

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

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

I have a class like:我有一个 class 像:

public abstract class BaseDao<T extends PersistentObject> {

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

  // ...

}

But the compiler says to T.class;但是编译器对T.class; : Illegal class literal for the type parameter T . : Illegal class literal for the type parameter T

How can I get the class of T ?如何获得T的 class ?

It's definitely possible to extract it from Class#getGenericSuperclass() because it's not defined during runtime, but during compiletime by FooDao extends BaseDao<Foo> .绝对可以从Class#getGenericSuperclass()提取它,因为它不是在运行时定义的,而是在编译时由FooDao extends BaseDao<Foo>

Here's a kickoff example how you could extract the desired generic super type in the constructor of the abstract class, taking a hierarchy of subclasses into account (along with a real world use case of applying it on generic EntityManager methods without the need to explicitly supply the type):这是一个启动示例,说明如何在抽象类的构造函数中提取所需的泛型超类型,同时考虑子类的层次结构(以及将其应用于泛型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();
    }

    // ...
}

Actually, this is not as easy as it seems.实际上,这并不像看起来那么容易。 There is a problem when you have rich type hierarchy and want to get generic parameter in the supertype.当您拥有丰富的类型层次结构并希望在超类型中获取泛型参数时,就会出现问题。 For example, you may have the following hierarchy:例如,您可能具有以下层次结构:

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

Calling getClass().getGenericSuperclass() in an instance of MyDao returns SpecialDao<TypeImpl, EntityImpl> , but when you call it inside BaseDao method, you don't know how deep the generic hierarchy is.MyDao的实例中调用getClass().getGenericSuperclass()返回SpecialDao<TypeImpl, EntityImpl> ,但是当你在BaseDao方法中调用它时,你不知道泛型层次结构有多深。 Moreover, as far as I know, you cannot obtain generic supertype of a supertype.此外,据我所知,您无法获得超类型的泛型超类型。 Thus, when you invoke getClass().getGenericSuperclass().getRawType().getGenericSuperclass() (with some typecasting omitted for readability), you'll get BaseDao<E> (notice <E> instead of <T> ).因此,当您调用getClass().getGenericSuperclass().getRawType().getGenericSuperclass() (为了可读性省略了一些类型转换)时,您将获得BaseDao<E> (注意<E>而不是<T> )。 Since getRawType() strips all type-variable mapping from the type, we're starting with unmapped type variables X and E .由于getRawType()从类型中剥离了所有类型变量映射,我们从未映射的类型变量XE Then getGenericSuperclass() just maps these type variables to their positions in BaseDao .然后getGenericSuperclass()只是将这些类型变量映射到它们在BaseDao的位置。

This behavior can be used so that we keep mapping from type variables to their actual values while traversing the type hierarchy.可以使用此行为,以便我们在遍历类型层次结构时保持从类型变量到它们的实际值的映射。 When we hit the class we want, we simply look up its type parameters in the map.当我们点击我们想要的类时,我们只需在地图中查找它的类型参数。 Here is the code:这是代码:

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

If Spring framework is available, you can do like here:如果 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);
    }

}

If your class is abstract, you can try with this:如果你的课程是抽象的,你可以试试这个:

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

This only work if the instance is a direct subclass , and the type of the class you want is the first one (see the [0]).这仅在实例是直接子类时有效,并且您想要的类的类型是第一个(参见 [0])。

If you have a large hierarchy of dao's, you can try fidn the BaseDao recursively and get the parametrized type如果您的 dao 层次结构很大,则可以尝试递归查找 BaseDao 并获取参数化类型

See a example here (see the output in the bottom)请参阅此处的示例(请参阅底部的输出)

Cheers and sorry for my bad english为我的英语不好而干杯和抱歉

Common way to sort this issue in a secure way is to add a constructor to store the class of the type.以安全的方式对这个问题进行排序的常用方法是添加一个构造函数来存储类型的类。 Example in your context:在您的上下文中的示例:

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

You can pass the type of class through the constructor of the abstract class, like this.您可以通过抽象 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;
    }

   // ...

}

And in the concrete class pass the class type in the constructor.在具体的 class 中,在构造函数中传递 class 类型。

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

The problem with this approach is that you cannot ensure that the type you are passing through the constructor is the actual type is being set in generic, so this can lead to issues if developers do not make sure they are passing the correct type through the constructor.这种方法的问题在于,您无法确保您通过构造函数传递的类型是在泛型中设置的实际类型,因此如果开发人员不确保他们通过构造函数传递正确的类型,这可能会导致问题.

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

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