Respuestas:
Como otros mencionaron, solo es posible mediante la reflexión en ciertas circunstancias.
Si realmente necesita el tipo, este es el patrón de solución habitual (seguro de tipo):
public class GenericClass<T> {
private final Class<T> type;
public GenericClass(Class<T> type) {
this.type = type;
}
public Class<T> getMyType() {
return this.type;
}
}
Foo foo1 = GetDao<Foo>(Foo.class).get(Foo.class, 1)
public static <T> GenericClass<T> of(Class<T> type) {...}
y luego llaman como tal: GenericClass<String> var = GenericClass.of(String.class)
. Un poco más amable
He visto algo como esto
private Class<T> persistentClass;
public Constructor() {
this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0];
}
en el ejemplo de hibernate GenericDataAccessObjects
class A implements Comparable<String>
, el parámetro de tipo real es String
, pero NO PUEDE decir que en Set<String> a = new TreeSet<String>()
, el parámetro de tipo real es String
. De hecho, la información del parámetro tipo se "borra" después de la compilación, como se explica en otras respuestas.
java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
esta respuesta.
(Class<T>) ((ParameterizedType)getClass().getSuperclass().getGenericSuperclass()).getActualTypeArguments()
para llegar a los argumentos de tipo real.
Los genéricos no se reifican en tiempo de ejecución. Esto significa que la información no está presente en tiempo de ejecución.
Agregar genéricos a Java mientras se mantenía la compatibilidad con versiones anteriores fue un tour-de-force (puede ver el documento seminal al respecto: Hacer el futuro seguro para el pasado: agregar genérico al lenguaje de programación Java ).
Hay una rica literatura sobre el tema, y algunas personas no están satisfechas con el estado actual, algunos dicen que en realidad es un señuelo y no hay una necesidad real de ello. Puedes leer ambos enlaces, los encontré bastante interesantes.
Usa guayaba.
import com.google.common.reflect.TypeToken;
import java.lang.reflect.Type;
public abstract class GenericClass<T> {
private final TypeToken<T> typeToken = new TypeToken<T>(getClass()) { };
private final Type type = typeToken.getType(); // or getRawType() to return Class<? super T>
public Type getType() {
return type;
}
public static void main(String[] args) {
GenericClass<String> example = new GenericClass<String>() { };
System.out.println(example.getType()); // => class java.lang.String
}
}
Hace un tiempo, publiqué algunos ejemplos de compromiso completo que incluyen clases abstractas y subclases aquí .
Nota: esto requiere que cree una instancia de una subclase de GenericClass
para que pueda vincular el parámetro de tipo correctamente. De lo contrario, solo devolverá el tipo como T
.
java.lang.IllegalArgumentException: class com.google.common.reflect.TypeToken isn't parameterized
. Entonces cambié la línea new TypeToken(getClass()) { }
a new TypeToken<T>(getClass()) { }
. Ahora, el código funciona bien, pero Type sigue siendo 'T'. Vea esto: gist.github.com/m-manu/9cda9d8f9d53bead2035
default Type getParameterType() { final TypeToken<T> typeToken = new TypeToken<T>(getClass()) {}; final Type type = typeToken.getType(); return type; }
GenericClass
, debe hacer esa clase abstract
para que el uso incorrecto no se compile.
Seguro que puede.
Java no utiliza la información en tiempo de ejecución, por razones de compatibilidad con versiones anteriores. Pero la información en realidad está presente como metadatos y se puede acceder a través de la reflexión (pero todavía no se utiliza para la verificación de tipos).
Desde la API oficial:
Sin embargo , para su escenario, no usaría la reflexión. Personalmente, estoy más inclinado a usar eso para el código marco. En su caso, simplemente agregaría el tipo como parámetro de constructor.
Los genéricos de Java son principalmente tiempo de compilación, esto significa que la información de tipo se pierde en tiempo de ejecución.
class GenericCls<T>
{
T t;
}
será compilado a algo como
class GenericCls
{
Object o;
}
Para obtener la información de tipo en tiempo de ejecución, debe agregarla como argumento del ctor.
class GenericCls<T>
{
private Class<T> type;
public GenericCls(Class<T> cls)
{
type= cls;
}
Class<T> getType(){return type;}
}
Ejemplo:
GenericCls<?> instance = new GenericCls<String>(String.class);
assert instance.getType() == String.class;
private final Class<T> type;
Type t = //String[]
public abstract class AbstractDao<T>
{
private final Class<T> persistentClass;
public AbstractDao()
{
this.persistentClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
}
}
Solía seguir enfoque:
public class A<T> {
protected Class<T> clazz;
public A() {
this.clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
public Class<T> getClazz() {
return clazz;
}
}
public class B extends A<C> {
/* ... */
public void anything() {
// here I may use getClazz();
}
}
No creo que pueda, Java utiliza el borrado de tipo al compilar para que su código sea compatible con las aplicaciones y bibliotecas que se crearon pre-genéricos.
De los documentos de Oracle:
Tipo de borrado
Se introdujeron genéricos en el lenguaje Java para proporcionar verificaciones de tipo más estrictas en el momento de la compilación y para admitir la programación genérica. Para implementar genéricos, el compilador de Java aplica el borrado de tipo a:
Reemplace todos los parámetros de tipo en tipos genéricos con sus límites u Objeto si los parámetros de tipo son ilimitados. El bytecode producido, por lo tanto, contiene solo clases, interfaces y métodos ordinarios. Inserte moldes de tipo si es necesario para preservar la seguridad de tipo. Genere métodos puente para preservar el polimorfismo en tipos genéricos extendidos. El borrado de tipos asegura que no se creen nuevas clases para tipos parametrizados; en consecuencia, los genéricos no incurren en gastos generales de tiempo de ejecución.
http://docs.oracle.com/javase/tutorial/java/generics/erasure.html
La técnica descrita en este artículo por Ian Robertson funciona para mí.
En resumen, un ejemplo rápido y sucio:
public abstract class AbstractDAO<T extends EntityInterface, U extends QueryCriteria, V>
{
/**
* Method returns class implementing EntityInterface which was used in class
* extending AbstractDAO
*
* @return Class<T extends EntityInterface>
*/
public Class<T> returnedClass()
{
return (Class<T>) getTypeArguments(AbstractDAO.class, getClass()).get(0);
}
/**
* Get the underlying class for a type, or null if the type is a variable
* type.
*
* @param type the type
* @return the underlying class
*/
public static Class<?> getClass(Type type)
{
if (type instanceof Class) {
return (Class) type;
} else if (type instanceof ParameterizedType) {
return getClass(((ParameterizedType) type).getRawType());
} else if (type instanceof GenericArrayType) {
Type componentType = ((GenericArrayType) type).getGenericComponentType();
Class<?> componentClass = getClass(componentType);
if (componentClass != null) {
return Array.newInstance(componentClass, 0).getClass();
} else {
return null;
}
} else {
return null;
}
}
/**
* Get the actual type arguments a child class has used to extend a generic
* base class.
*
* @param baseClass the base class
* @param childClass the child class
* @return a list of the raw classes for the actual type arguments.
*/
public static <T> List<Class<?>> getTypeArguments(
Class<T> baseClass, Class<? extends T> childClass)
{
Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
Type type = childClass;
// start walking up the inheritance hierarchy until we hit baseClass
while (!getClass(type).equals(baseClass)) {
if (type instanceof Class) {
// there is no useful information for us in raw types, so just keep going.
type = ((Class) type).getGenericSuperclass();
} else {
ParameterizedType parameterizedType = (ParameterizedType) type;
Class<?> rawType = (Class) parameterizedType.getRawType();
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
for (int i = 0; i < actualTypeArguments.length; i++) {
resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
}
if (!rawType.equals(baseClass)) {
type = rawType.getGenericSuperclass();
}
}
}
// finally, for each actual type argument provided to baseClass, determine (if possible)
// the raw class for that type argument.
Type[] actualTypeArguments;
if (type instanceof Class) {
actualTypeArguments = ((Class) type).getTypeParameters();
} else {
actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
}
List<Class<?>> typeArgumentsAsClasses = new ArrayList<Class<?>>();
// resolve types by chasing down type variables.
for (Type baseType : actualTypeArguments) {
while (resolvedTypes.containsKey(baseType)) {
baseType = resolvedTypes.get(baseType);
}
typeArgumentsAsClasses.add(getClass(baseType));
}
return typeArgumentsAsClasses;
}
}
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
Creo que hay otra solución elegante.
Lo que quiere hacer es (con seguridad) "pasar" el tipo del parámetro de tipo genérico desde la clase concerete a la superclase.
Si te permites pensar en el tipo de clase como "metadatos" en la clase, eso sugiere el método Java para codificar metadatos en tiempo de ejecución: anotaciones.
Primero defina una anotación personalizada a lo largo de estas líneas:
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EntityAnnotation {
Class entityClass();
}
Entonces puede tener que agregar la anotación a su subclase.
@EntityAnnotation(entityClass = PassedGenericType.class)
public class Subclass<PassedGenericType> {...}
Luego puede usar este código para obtener el tipo de clase en su clase base:
import org.springframework.core.annotation.AnnotationUtils;
.
.
.
private Class getGenericParameterType() {
final Class aClass = this.getClass();
EntityAnnotation ne =
AnnotationUtils.findAnnotation(aClass, EntityAnnotation.class);
return ne.entityClass();
}
Algunas limitaciones de este enfoque son:
PassedGenericType
) en DOS lugares en lugar de uno que no sea SECO.Esta es mi solución:
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
public class GenericClass<T extends String> {
public static void main(String[] args) {
for (TypeVariable typeParam : GenericClass.class.getTypeParameters()) {
System.out.println(typeParam.getName());
for (Type bound : typeParam.getBounds()) {
System.out.println(bound);
}
}
}
}
¡Aquí está la solución de trabajo!
@SuppressWarnings("unchecked")
private Class<T> getGenericTypeClass() {
try {
String className = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0].getTypeName();
Class<?> clazz = Class.forName(className);
return (Class<T>) clazz;
} catch (Exception e) {
throw new IllegalStateException("Class is not parametrized with generic type!!! Please use extends <> ");
}
}
NOTAS:
Solo se puede usar como superclase
1. Se debe ampliar con la clase escrita ( Child extends Generic<Integer>
)
O
2. Se debe crear como implementación anónima ( new Generic<Integer>() {};
)
Aquí hay una forma, que he tenido que usar una o dos veces:
public abstract class GenericClass<T>{
public abstract Class<T> getMyType();
}
Junto con
public class SpecificClass extends GenericClass<String>{
@Override
public Class<String> getMyType(){
return String.class;
}
}
Una solución simple para esta cabina es la siguiente
public class GenericDemo<T>{
private T type;
GenericDemo(T t)
{
this.type = t;
}
public String getType()
{
return this.type.getClass().getName();
}
public static void main(String[] args)
{
GenericDemo<Integer> obj = new GenericDemo<Integer>(5);
System.out.println("Type: "+ obj.getType());
}
}
Para completar algunas de las respuestas aquí, tuve que obtener el ParametrizedType de MyGenericClass, sin importar qué tan alta sea la jerarquía, con la ayuda de la recursividad:
private Class<T> getGenericTypeClass() {
return (Class<T>) (getParametrizedType(getClass())).getActualTypeArguments()[0];
}
private static ParameterizedType getParametrizedType(Class clazz){
if(clazz.getSuperclass().equals(MyGenericClass.class)){ // check that we are at the top of the hierarchy
return (ParameterizedType) clazz.getGenericSuperclass();
} else {
return getParametrizedType(clazz.getSuperclass());
}
}
Aquí está mi truco:
public class Main {
public static void main(String[] args) throws Exception {
System.out.println(Main.<String> getClazz());
}
static <T> Class getClazz(T... param) {
return param.getClass().getComponentType();
}
}
T
es una variable de tipo. En el caso de que T
sea una variable de tipo, los varargs crean una matriz de borrado de T
. Ver, por ejemplo, http://ideone.com/DIPNwd .
En caso de que use almacenar una variable usando el tipo genérico, puede resolver fácilmente este problema agregando un método getClassType de la siguiente manera:
public class Constant<T> {
private T value;
@SuppressWarnings("unchecked")
public Class<T> getClassType () {
return ((Class<T>) value.getClass());
}
}
Utilizo el objeto de clase proporcionado más adelante para verificar si es una instancia de una clase dada, de la siguiente manera:
Constant<?> constant = ...;
if (constant.getClassType().equals(Integer.class)) {
Constant<Integer> integerConstant = (Constant<Integer>)constant;
Integer value = integerConstant.getValue();
// ...
}
value
es así null
? En segundo lugar, ¿qué value
pasa si es una subclase de T
? Constant<Number> c = new Constant<Number>(new Integer(0)); Class<Number> n = c.getClassType();
regresa Integer.class
cuando debería regresar Number.class
. Sería más correcto regresar Class<? extends T>
. Integer.class
es un Class<? extends Number>
pero no un Class<Number>
.
Aqui esta mi solucion
public class GenericClass<T>
{
private Class<T> realType;
public GenericClass() {
findTypeArguments(getClass());
}
private void findTypeArguments(Type t) {
if (t instanceof ParameterizedType) {
Type[] typeArgs = ((ParameterizedType) t).getActualTypeArguments();
realType = (Class<T>) typeArgs[0];
} else {
Class c = (Class) t;
findTypeArguments(c.getGenericSuperclass());
}
}
public Type getMyType()
{
// How do I return the type of T? (your question)
return realType;
}
}
No importa cuántos niveles tenga su jerarquía de clases, esta solución aún funciona, por ejemplo:
public class FirstLevelChild<T> extends GenericClass<T> {
}
public class SecondLevelChild extends FirstLevelChild<String> {
}
En este caso, getMyType () = java.lang.String
Exception in thread "main" java.lang.NullPointerException at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.<init>(Main.java:43) at Main.main(Main.java:61)
Aquí está mi solución. Los ejemplos deberían explicarlo. El único requisito es que una subclase debe establecer el tipo genérico, no un objeto.
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.Map;
public class TypeUtils {
/*** EXAMPLES ***/
public static class Class1<A, B, C> {
public A someA;
public B someB;
public C someC;
public Class<?> getAType() {
return getTypeParameterType(this.getClass(), Class1.class, 0);
}
public Class<?> getCType() {
return getTypeParameterType(this.getClass(), Class1.class, 2);
}
}
public static class Class2<D, A, B, E, C> extends Class1<A, B, C> {
public B someB;
public D someD;
public E someE;
}
public static class Class3<E, C> extends Class2<String, Integer, Double, E, C> {
public E someE;
}
public static class Class4 extends Class3<Boolean, Long> {
}
public static void test() throws NoSuchFieldException {
Class4 class4 = new Class4();
Class<?> typeA = class4.getAType(); // typeA = Integer
Class<?> typeC = class4.getCType(); // typeC = Long
Field fieldSomeA = class4.getClass().getField("someA");
Class<?> typeSomeA = TypeUtils.getFieldType(class4.getClass(), fieldSomeA); // typeSomeA = Integer
Field fieldSomeE = class4.getClass().getField("someE");
Class<?> typeSomeE = TypeUtils.getFieldType(class4.getClass(), fieldSomeE); // typeSomeE = Boolean
}
/*** UTILS ***/
public static Class<?> getTypeVariableType(Class<?> subClass, TypeVariable<?> typeVariable) {
Map<TypeVariable<?>, Type> subMap = new HashMap<>();
Class<?> superClass;
while ((superClass = subClass.getSuperclass()) != null) {
Map<TypeVariable<?>, Type> superMap = new HashMap<>();
Type superGeneric = subClass.getGenericSuperclass();
if (superGeneric instanceof ParameterizedType) {
TypeVariable<?>[] typeParams = superClass.getTypeParameters();
Type[] actualTypeArgs = ((ParameterizedType) superGeneric).getActualTypeArguments();
for (int i = 0; i < typeParams.length; i++) {
Type actualType = actualTypeArgs[i];
if (actualType instanceof TypeVariable) {
actualType = subMap.get(actualType);
}
if (typeVariable == typeParams[i]) return (Class<?>) actualType;
superMap.put(typeParams[i], actualType);
}
}
subClass = superClass;
subMap = superMap;
}
return null;
}
public static Class<?> getTypeParameterType(Class<?> subClass, Class<?> superClass, int typeParameterIndex) {
return TypeUtils.getTypeVariableType(subClass, superClass.getTypeParameters()[typeParameterIndex]);
}
public static Class<?> getFieldType(Class<?> clazz, AccessibleObject element) {
Class<?> type = null;
Type genericType = null;
if (element instanceof Field) {
type = ((Field) element).getType();
genericType = ((Field) element).getGenericType();
} else if (element instanceof Method) {
type = ((Method) element).getReturnType();
genericType = ((Method) element).getGenericReturnType();
}
if (genericType instanceof TypeVariable) {
Class<?> typeVariableType = TypeUtils.getTypeVariableType(clazz, (TypeVariable) genericType);
if (typeVariableType != null) {
type = typeVariableType;
}
}
return type;
}
}
Puede ser útil para alguien. Puede usar java.lang.ref.WeakReference; de esta manera:
class SomeClass<N>{
WeakReference<N> variableToGetTypeFrom;
N getType(){
return variableToGetTypeFrom.get();
}
}
WeakReference
? Proporcione alguna explicación con su respuesta, no solo un código.
SomeClass<MyClass>
, puede crear instancias SomeClass
y llamar getType
a esa instancia y tener el tiempo de ejecución MyClass
.
WeakReference
? Lo que dijiste no es diferente de la mayoría de las otras respuestas.
AtomicReference
, List
, Set
).
Encontré que esta es una solución simple, comprensible y fácil de explicar.
public class GenericClass<T> {
private Class classForT(T...t) {
return t.getClass().getComponentType();
}
public static void main(String[] args) {
GenericClass<String> g = new GenericClass<String>();
System.out.println(g.classForT());
System.out.println(String.class);
}
}
(T...t)
. (Es por eso que este código no funciona.)