Obtenga un tipo genérico de clase en tiempo de ejecución

508

¿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 en Objectlugar del tipo específico utilizado.

Glenn
fuente
3
@SamuelVidal Esto no es .NET, es Java.
Frontear

Respuestas:

317

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;
     }
}
Henning
fuente
58
Me gusta esta respuesta, pero es un poco engorroso crear una instancia: GenericClass <AnotherClass> g = new GenericClass <AnotherClass> (AnotherClass.class);
Eliseo Ocampos
1
Es aún más detallado si utiliza un enfoque dao / factory / manager. Foo foo1 = GetDao<Foo>(Foo.class).get(Foo.class, 1)
djmj
2
Eso es cierto, pero no funciona en todos los casos, como beans remotos sin estado que se instancian por contenedor / reflexión.
djmj
66
Solo como seguimiento de mi comentario anterior: después de mucho dolor jugando con la reflexión, terminé usando esta respuesta.
Tomáš Zato - Restablece a Monica
18
Puede evitar la referencia superflua proporcionando un método genérico de fábrica estática. Algo así como 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
Joeri Hendrickx
263

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

FrVaBe
fuente
84
Esta técnica funciona donde el parámetro de tipo se define en la superclase inmediata, pero falla si el parámetro de tipo se define en otra parte de la jerarquía de tipos. Para manejar casos más complejos se puede usar algo como TypeTools . Los documentos incluyen un ejemplo de un DAO genérico más sofisticado.
Jonathan
25
Esto sólo devuelve los parámetros de tipo real utilizado cuando una CLASE implementos / extiende algo que tiene declaraciones genéricas, que no devuelve los parámetros de tipo real utilizado cuando un INSTANCIA se instancia. En otras palabras, PUEDE decir que en 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.
Siu Ching Pong -Asuka Kenji-
32
Estoy obteniendo java.lang.Class cannot be cast to java.lang.reflect.ParameterizedTypeesta respuesta.
Tomáš Zato - Restablece a Monica
44
Este enfoque también se puede lograr con Class-Matela gente de Jackson. Escribí un resumen
yunspace
55
@ TomášZato Llamar simplemente al código anterior me devolvió la misma excepción. Sé que es un poco tarde, pero de todos modos, en mi caso, tuve que llamar (Class<T>) ((ParameterizedType)getClass().getSuperclass().getGenericSuperclass()).getActualTypeArguments()para llegar a los argumentos de tipo real.
AndrewMcCoist
99

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.

ewernli
fuente
179
Por supuesto que no estamos satisfechos, .NET tiene un mecanismo de manejo genérico mucho mejor
Pacerier el
66
@Pacerier: pero los genéricos reificados por sí solos no llevarían a Java al nivel de .NET. Los tipos de valor de un código especializado para ellos son al menos igualmente importantes por qué .NET es mejor en el área genérica.
Joachim Sauer
@JoachimSauer, sí tipos de valor. Siempre quise los de Java. Por cierto, ¿qué quieres decir con código especializado?
Pacerier
3
@ spaaarky21 No, los parámetros de tipo genérico se eliminan durante la compilación (llamada "borrado", puede buscarlo en Google). El truco en la respuesta de FrVaBe funciona solo si los parámetros de tipo de la superclase se conocen estáticamente (ver el primer comentario de Johnathn)
ewernli
1
El borrado de tipo Java es un defecto de diseño histórico; se ha escrito más código para evitarlo del que se escribió para implementarlo.
Abhijit Sarkar
68

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 GenericClasspara que pueda vincular el parámetro de tipo correctamente. De lo contrario, solo devolverá el tipo como T.

Cody A. Ray
fuente
3
Tenga en cuenta que creo una subclase anónima vacía (vea las dos llaves al final). Esto utiliza la reflexión para combatir el borrado del tipo de tiempo de ejecución de Java. Puede obtener más información aquí: code.google.com/p/guava-libraries/wiki/ReflectionExplained
Cody A. Ray
3
@ CodyA.Ray Su código arroja a 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
Manu Manjunath
3
@Dominik Consulte el ejemplo actualizado que puede copiar y pegar para probarse. También he agregado una nota que aclara que debe crear una instancia de una subclase (como se muestra). Como consejo general de etiqueta, lea los artículos vinculados y los javadocs relacionados antes de acusar a un afiche de "ilusiones". He usado un código de producción similar varias veces. Los ayudantes de guayaba que estoy demostrando están destinados a este caso de uso exacto y sus javadocs muestran una respuesta casi exacta a esta pregunta. docs.guava-libraries.googlecode.com/git/javadoc/com/google/…
Cody A. Ray
1
Esta respuesta funciona muy bien con Java8: tengo una interfaz y una clase de fábrica para emitir varios tipos, y quería que los usuarios pudieran determinar fácilmente el tipo. En mi interfaz agregué el método predeterminado: default Type getParameterType() { final TypeToken<T> typeToken = new TypeToken<T>(getClass()) {}; final Type type = typeToken.getType(); return type; }
Richard Sand
2
@ CodyA.Ray Dado que esto solo funciona con subclases de GenericClass, debe hacer esa clase abstractpara que el uso incorrecto no se compile.
Martin
37

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:

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

Joeri Hendrickx
fuente
10
getActualTypeArguments solo devuelve los argumentos de tipo para la clase inmediata. Si tiene una jerarquía de tipos compleja en la que T podría parametrizarse en cualquier parte de la jerarquía, deberá hacer un poco de trabajo para descubrir qué es. Esto es más o menos lo que hace TypeTools .
Jonathan
36

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;
josefx
fuente
99
private final Class<T> type;
naXa
¿Cómo puedo crear un tipo de matriz a partir de él?Type t = //String[]
Pawel Cioch
@PawelCioch java.lang.reflect.Array.newInstance (elementtype, length); espero que esto ayude (javadoc se puede encontrar aquí docs.oracle.com/javase/8/docs/api/java/lang/reflect/… )
josefx
@PawelCioch perdió un .getClass () para obtener el tipo de la matriz creada. No parece haber una forma directa de obtener una clase de matriz. La mayoría de las colecciones de Java solo usan Object [] en su lugar.
josefx
23
public abstract class AbstractDao<T>
{
    private final Class<T> persistentClass;

    public AbstractDao()
    {
        this.persistentClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass())
                .getActualTypeArguments()[0];
    }
}
Ali Yeganeh
fuente
3
Estoy votando esta respuesta porque es una solución que funciona para la pregunta que se hace. Sin embargo, para aquellos que quieren navegar hacia arriba en la jerarquía de clases como yo con más de una clase genérica, esto no funcionará. Porque obtendrás java.lang.object en lugar de la clase real.
KMC
3
Tenga en cuenta que esta solución funciona SOLO si la clase que contiene el tipo genérico es RESUMEN
BabaNuevo
No funcionó con mi clase abstracta en Java 11
JRA_TLL
@JRA_TLL aparentemente hiciste algo mal. Lo acabo de usar con Java 12 y funciona de maravilla.
Googie
Si desea navegar hacia arriba en la jerarquía de vistas, puede convertir el genericSuperclass en Class <*> y obtener el genericSuperclass. Preferiblemente en un bucle.
Fupduck
21

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();
    }
}
Moesio
fuente
99
Obtuve "Exception in thread" main "java.lang.ClassCastException: java.lang.Class no se puede
enviar
16

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

Dimitar
fuente
Jup, es imposible. Java necesitaría genéricos reificados para que eso funcione.
Henning
15

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;
    }
  }
Ondrej Bozek
fuente
3
¿En qué línea específica de este código se leen los parámetros reales de tipo de tiempo de ejecución?
Ivan Matavulj
¿aquí? Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
Ondrej Bozek
9

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:

  1. Usted especifica el tipo genérico (PassedGenericType ) en DOS lugares en lugar de uno que no sea SECO.
  2. Esto solo es posible si puede modificar las subclases concretas.
DaBlick
fuente
Sí, no es SECO, sin embargo, es más limpio que el enfoque de extensión sugerido anteriormente. Me gustó. gracias
agodinhost
8

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);
      }
    }
  }
}
Matthias M
fuente
10
Esta no es una respuesta para esta pregunta.
Adam Arold
1
Mi código no es la solución exacta para la pregunta. Devuelve los parámetros de tipo genérico de la clase, pero no el tipo real de T. Pero puede ser útil para otras personas que se atontan a la pregunta y están buscando mi solución.
Matthias M
1
getClass (). getGenericSuperclass () logrará el mismo efecto.
Gorki
5

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

Yaroslav Kovbas
fuente
4

No puedes Si agrega una variable miembro de tipo T a la clase (ni siquiera tiene que inicializarla), podría usarla para recuperar el tipo.

andrewmu
fuente
2
Vaya, está bien. Usted no tiene que inicializar desde un constructor.
andrewmu
4

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;
    }
}
PJWeisberg
fuente
44
Esto técnicamente funciona, sin embargo, no resuelve el caso general, y creo que eso es lo que busca el póster original.
ggb667
Esto no merece ser rechazado como es: el póster original no ha sido explícito. Esta respuesta ofrece un patrón de diseño que hace el trabajo y es fácil de implementar, siempre es conveniente hacer la clase genérica abstracta.
VirtualMichael
4

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

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

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

}
HRgiger
fuente
1
Nota: esto no funciona cuando Tes una variable de tipo. En el caso de que Tsea ​​una variable de tipo, los varargs crean una matriz de borrado de T. Ver, por ejemplo, http://ideone.com/DIPNwd .
Radiodef
1
Esto devuelve "Objeto"
yahya
Puede ser que estés tratando de responder alguna otra pregunta 🤔
Khan
1

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();
    // ...
}
Pablo Trinidad
fuente
2
Esto es problemático, desafortunadamente. En primer lugar, ¿y si valuees así null? En segundo lugar, ¿qué valuepasa si es una subclase de T? Constant<Number> c = new Constant<Number>(new Integer(0)); Class<Number> n = c.getClassType();regresa Integer.classcuando 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>.
Radiodef
1

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

Lin Yu Cheng
fuente
Esto no está devolviendo el tipo de T. Está devolviendo T no java.lang.String además del código no puede convertir el tipo a la clase <T>
Mohy Eldeen
Aquí hay una muestra en línea que hice. Haga clic en compilar y ejecutar, luego puede obtener el resultado. tutorialspoint.com/…
Lin Yu Cheng
Funciona para mí, cuando WildFly Weld CDI rompió un método alternativo.
Andre
TengoException 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)
yuyang
0
public static final Class<?> getGenericArgument(final Class<?> clazz)
{
    return (Class<?>) ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[0];
}
kayz1
fuente
0

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

}
PetermaxX
fuente
-1

Hice lo mismo que @Moesio Above pero en Kotlin podría hacerse de esta manera:

class A<T : SomeClass>() {

    var someClassType : T

    init(){
    this.someClassType = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class<T>
    }

}
Bonestack
fuente
-4

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();
  }
}
TheLetch
fuente
¿Cómo se supone que debe usarse esta clase? ¿Por qué WeakReference? Proporcione alguna explicación con su respuesta, no solo un código.
Abhijit Sarkar
Entonces, si tiene un SomeClass<MyClass> , puede crear instancias SomeClassy llamar getTypea esa instancia y tener el tiempo de ejecución MyClass.
TheLetch
Claro, pero ¿por qué WeakReference? Lo que dijiste no es diferente de la mayoría de las otras respuestas.
Abhijit Sarkar
Primero, mi enfoque es más corto (menos código), segundo, las referencias débiles no impiden que sus referentes sean finalizables, y que yo sepa, no utiliza la reflexión, por lo tanto, es rápido
TheLetch
Esto no consigue el tipo de nada, este devuelve un objeto de ese tipo, que, para tu información, se puede hacer con literalmente cualquier tipo de envoltorio ( AtomicReference, List, Set).
Frontear
-5

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);
    }
}
Mark Karchner
fuente
Explicar (T...t). (Es por eso que este código no funciona.)
Christopher Schneider