java - programacion - reflexion spring




Obtener el tipo genérico de clase en tiempo de ejecución (16)

¿Cómo puedo conseguir esto?

public class GenericClass<T>
{
    public Type getMyType()
    {
        //How do I return the type of T?
    }
}

Todo lo que he intentado hasta ahora siempre devuelve el tipo Object lugar del tipo específico utilizado.


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: Puede ser usado solo como superclase
1. Tiene que extenderse con la clase escrita (el Child extends Generic<Integer> )
O
2. Debe crearse como implementación anónima ( new Generic<Integer>() {}; )


Aquí está mi solución

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 todavía funciona, por ejemplo:

public class FirstLevelChild<T> extends GenericClass<T> {

}

public class SecondLevelChild extends FirstLevelChild<String> {

}

En este caso, getMyType () = java.lang.String


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

Como han mencionado otros, solo es posible a través de la reflexión en ciertas circunstancias.

Si realmente necesita el tipo, este es el patrón de solución habitual (seguro para el 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;
     }
}

En caso de que utilice almacenar una variable utilizando el tipo genérico, puede resolver este problema fácilmente 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();
    // ...
}

Encontré que esta es una solución simple, comprensible y fácilmente explicable.

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

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


La técnica descrita en este artima.com/weblogs/viewpost.jsp?thread=208860 funciona para mí.

En breve 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;
    }
  }

Los genéricos no son reificados 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 era un tour-de-force (puede ver el documento seminal sobre esto: hacer que el futuro sea seguro para el pasado: agregar genéricoidad al lenguaje de programación Java ).

Hay una rica literatura sobre el tema, y ​​algunas personas están dissatisfied con el estado actual, algunas dicen que en realidad es un lure y que no hay una necesidad real de hacerlo. Puedes leer ambos enlaces, me parecieron bastante interesantes.


No creo que puedas, Java utiliza el borrado de tipos al compilar, por lo que tu código es compatible con las aplicaciones y bibliotecas que se crearon de forma pre-generica.

De los documentos de Oracle:

Tipo de borrado

Los genéricos se introdujeron en el lenguaje Java para proporcionar controles de tipo más estrictos 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 los 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 tipo si es necesario para preservar la seguridad del tipo. Genere métodos de puente para preservar el polimorfismo en tipos genéricos extendidos. El borrado de tipos garantiza que no se creen nuevas clases para tipos parametrizados; en consecuencia, los genéricos no incurren en gastos generales de ejecución.

http://docs.oracle.com/javase/tutorial/java/generics/erasure.html


Podría ser útil para alguien. Puedes usar java.lang.ref.WeakReference; de esta manera:

class SomeClass<N>{
  WeakReference<N> variableToGetTypeFrom;

  N getType(){
    return variableToGetTypeFrom.get();
  }
}

Seguro que puede.

Java no usa 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 aún no se utiliza para la comprobación de tipos).

Desde la API oficial:

http://download.oracle.com/javase/6/docs/api/java/lang/reflect/ParameterizedType.html#getActualTypeArguments%28%29

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 constructor.


Usé el enfoque de seguimiento:

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

Usted no puede Si agrega una variable miembro de tipo T a la clase (ni siquiera tiene que inicializarla), puede usarla para recuperar el tipo.


public abstract class AbstractDao<T> <br>
{

    private final Class<T> persistentClass;


    public AbstractDao()
    {
        this.persistentClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass())
                .getActualTypeArguments()[0];
    }
}

public static final Class<?> getGenericArgument(final Class<?> clazz)
{
    return (Class<?>) ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[0];
}




reflection