Obtenga un tipo real de argumento de tipo genérico en la superclase abstracta

Tengo una clase como:

public abstract class BaseDao { protected Class getClazz() { return T.class; } // ... } 

Pero el comstackdor le dice a T.class; : Illegal class literal for the type parameter T

¿Cómo puedo obtener la clase de T ?

Definitivamente es posible extraerlo de Class#getGenericSuperclass() porque no está definido durante el tiempo de ejecución, pero durante el tiempo de FooDao extends BaseDao de FooDao extends BaseDao .

Aquí hay un ejemplo inicial de cómo se puede extraer el super tipo genérico deseado en el constructor de la clase abstracta, teniendo en cuenta una jerarquía de subclases (junto con un caso de uso del mundo real de aplicarlo en métodos generics de EntityManager sin la necesidad de proporcionar explícitamente el tipo):

 public abstract class BaseDao { @PersistenceContext private EntityManager em; private Class type; @SuppressWarnings("unchecked") // For the cast on Class. 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) ((ParameterizedType) type).getActualTypeArguments()[0]; } public E find(Long id) { return em.find(type, id); } public List list() { return em.createQuery(String.format("SELECT e FROM %se ORDER BY id", type.getSimpleName()), type).getResultList(); } // ... } 

En realidad, esto no es tan fácil como parece. Hay un problema cuando tiene una jerarquía de tipos enriquecidos y desea obtener un parámetro genérico en el supertipo. Por ejemplo, puede tener la siguiente jerarquía:

 public abstract class BaseDao { ... } public abstract class SpecialDao extends BaseDao { ... } public class MyDao extends SpecialDao { ... } 

Llamar a getClass().getGenericSuperclass() en una instancia de MyDao devuelve SpecialDao , pero cuando lo llama dentro del método BaseDao , no sabe qué tan profunda es la jerarquía genérica. Además, hasta donde yo sé, no se puede obtener un supertipo genérico de un supertipo. Por lo tanto, cuando invoque getClass().getGenericSuperclass().getRawType().getGenericSuperclass() (con algunos getClass().getGenericSuperclass().getRawType().getGenericSuperclass() omitidos para la legibilidad), obtendrá BaseDao (observe lugar de ). Como getRawType() todas las asignaciones de variables de tipo del tipo, estamos comenzando con las variables de tipo X y E getRawType() . Entonces getGenericSuperclass() simplemente asigna estas variables de tipo a sus posiciones en BaseDao .

Este comportamiento se puede usar para que sigamos asignando variables de tipo a sus valores reales mientras atravesamos la jerarquía de tipos. Cuando llegamos a la clase que queremos, simplemente buscamos sus parámetros de tipo en el mapa. Aquí está el código:

 @SuppressWarnings("unchecked") public static  Class getGenericClassParameter(final Class< ?> parameterizedSubClass, final Class< ?> genericSuperClass, final int pos) { // a mapping from type variables to actual values (classes) Map, 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; } else { return (Class) 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); } 

Si Spring framework está disponible, puede hacer lo siguiente aquí:

 import org.springframework.core.GenericTypeResolver; public abstract class BaseDao { protected Class getClazz() { return (Class) GenericTypeResolver.resolveTypeArgument(getClass(), BaseDao.class); } } 

Si tu clase es abstracta, puedes intentar con esto:

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

Esto solo funciona si la instancia es una subclase directa , y el tipo de la clase que desea es el primero (vea [0]).

Si tiene una gran jerarquía de dao, puede intentar encontrar el BaseDao recursivamente y obtener el tipo parametrizado

Vea un ejemplo aquí (vea la salida en la parte inferior)

Saludos y perdón por mi mal inglés

La forma común de ordenar este problema de forma segura es agregar un constructor para almacenar la clase del tipo. Ejemplo en tu contexto:

 public abstract class BaseDao { private Class classT; BaseDao(Class classT){ this.classT=classT; } protected Class getClazz() { return classT; } // ... } 

Puede ver TypeTools para esto:

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