java - tipos - Cómo obtener una instancia de clase de genéricos tipo T




tipos de datos genericos en java (12)

Aquí hay una 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. Debe extenderse con la clase escrita ( Child extends Generic<Integer> )

O

  1. Debe crearse como implementación anónima ( new Generic<Integer>() {}; )

https://code.i-harness.com

Tengo una clase de genéricos, Foo<T> . En un método de Foo , quiero obtener la instancia de clase de tipo T, pero simplemente no puedo llamar a T.class .

¿Cuál es la forma preferida de T.class usando T.class ?


Como se explica en otras respuestas, para usar este enfoque de tipo de ParameterizedType , necesita extender la clase, pero parece ser un trabajo extra para crear una clase completamente nueva que lo extienda ...

Entonces, al hacer que la clase sea abstracta, obliga a extenderla, satisfaciendo así el requisito de subclasificación. (usando @Getter de lombok).

@Getter
public abstract class ConfigurationDefinition<T> {

    private Class<T> type;
    ...

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

Ahora extenderlo sin definir una nueva clase. (Tenga en cuenta que {} al final ... extendido, pero no sobrescriba nada, a menos que quiera).

private ConfigurationDefinition<String> myConfigA = new ConfigurationDefinition<String>(...){};
private ConfigurationDefinition<File> myConfigB = new ConfigurationDefinition<File>(...){};
...
Class stringType = myConfigA.getType();
Class fileType = myConfigB.getType();

Encontré una forma genérica y simple de hacer eso. En mi clase, creé un método que devuelve el tipo genérico de acuerdo con su posición en la definición de clase. Asumamos una definición de clase como esta:

public class MyClass<A, B, C> {

}

Ahora vamos a crear algunos atributos para persistir los tipos:

public class MyClass<A, B, C> {

    private Class<A> aType;

    private Class<B> bType;

    private Class<C> cType;

// Getters and setters (not necessary if you are going to use them internally)

    } 

Luego, puede crear un método genérico que devuelva el tipo basado en el índice de la definición genérica:

   /**
     * Returns a {@link Type} object to identify generic types
     * @return type
     */
    private Type getGenericClassType(int index) {
        // To make it use generics without supplying the class type
        Type type = getClass().getGenericSuperclass();

        while (!(type instanceof ParameterizedType)) {
            if (type instanceof ParameterizedType) {
                type = ((Class<?>) ((ParameterizedType) type).getRawType()).getGenericSuperclass();
            } else {
                type = ((Class<?>) type).getGenericSuperclass();
            }
        }

        return ((ParameterizedType) type).getActualTypeArguments()[index];
    }

Finalmente, en el constructor simplemente llame al método y envíe el índice para cada tipo. El código completo debe verse como:

public class MyClass<A, B, C> {

    private Class<A> aType;

    private Class<B> bType;

    private Class<C> cType;


    public MyClass() {
      this.aType = (Class<A>) getGenericClassType(0);
      this.bType = (Class<B>) getGenericClassType(1);
      this.cType = (Class<C>) getGenericClassType(2);
    }

   /**
     * Returns a {@link Type} object to identify generic types
     * @return type
     */
    private Type getGenericClassType(int index) {

        Type type = getClass().getGenericSuperclass();

        while (!(type instanceof ParameterizedType)) {
            if (type instanceof ParameterizedType) {
                type = ((Class<?>) ((ParameterizedType) type).getRawType()).getGenericSuperclass();
            } else {
                type = ((Class<?>) type).getGenericSuperclass();
            }
        }

        return ((ParameterizedType) type).getActualTypeArguments()[index];
    }
}


Estoy usando una solución para esto:

class MyClass extends Foo<T> {
....
}

MyClass myClassInstance = MyClass.class.newInstance();

Imagina que tienes una superclase abstracta que es genérica:

public abstract class Foo<? extends T> {}

Y luego tienes una segunda clase que extiende Foo con una barra genérica que extiende T:

public class Second extends Foo<Bar> {}

Puede obtener la clase Bar.class en la clase Foo seleccionando el Type (de la respuesta de bert bruynooghe) e inferiéndolo usando la instancia de la Class :

Type mySuperclass = myFoo.getClass().getGenericSuperclass();
Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
//Parse it as String
String className = tType.toString().split(" ")[1];
Class clazz = Class.forName(className);

Debe tener en cuenta que esta operación no es la ideal, por lo que es una buena idea almacenar en caché el valor computado para evitar cálculos múltiples sobre esto. Uno de los usos típicos es en la implementación genérica de DAO.

La implementación final:

public abstract class Foo<T> {

    private Class<T> inferedClass;

    public Class<T> getGenericClass(){
        if(inferedClass == null){
            Type mySuperclass = getClass().getGenericSuperclass();
            Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
            String className = tType.toString().split(" ")[1];
            inferedClass = Class.forName(className);
        }
        return inferedClass;
    }
}

El valor devuelto es Bar.class cuando se invoca desde la clase Foo en otra función o desde la clase Bar.



Si está extendiendo o implementando cualquier clase / interfaz que esté usando genéricos, puede obtener el Tipo genérico de clase / interfaz padre, sin modificar ninguna clase / interfaz existente.

Podría haber tres posibilidades,

Caso 1 Cuando su clase está extendiendo una clase que está usando Genéricos

public class TestGenerics {
    public static void main(String[] args) {
        Type type = TestMySuperGenericType.class.getGenericSuperclass();
        Type[] gTypes = ((ParameterizedType)type).getActualTypeArguments();
        for(Type gType : gTypes){
            System.out.println("Generic type:"+gType.toString());
        }
    }
}

class GenericClass<T> {
    public void print(T obj){};
}

class TestMySuperGenericType extends GenericClass<Integer> {
}

Caso 2 Cuando su clase está implementando una interfaz que está usando Genéricos

public class TestGenerics {
    public static void main(String[] args) {
        Type[] interfaces = TestMySuperGenericType.class.getGenericInterfaces();
        for(Type type : interfaces){
            Type[] gTypes = ((ParameterizedType)type).getActualTypeArguments();
            for(Type gType : gTypes){
                System.out.println("Generic type:"+gType.toString());
            }
        }
    }
}

interface GenericClass<T> {
    public void print(T obj);
}

class TestMySuperGenericType implements GenericClass<Integer> {
    public void print(Integer obj){}
}

Caso 3 Cuando su interfaz está extendiendo una interfaz que usa Genéricos

public class TestGenerics {
    public static void main(String[] args) {
        Type[] interfaces = TestMySuperGenericType.class.getGenericInterfaces();
        for(Type type : interfaces){
            Type[] gTypes = ((ParameterizedType)type).getActualTypeArguments();
            for(Type gType : gTypes){
                System.out.println("Generic type:"+gType.toString());
            }
        }
    }
}

interface GenericClass<T> {
    public void print(T obj);
}

interface TestMySuperGenericType extends GenericClass<Integer> {
}

Tengo una solución (fea pero efectiva) para este problema, que utilicé recientemente:

import java.lang.reflect.TypeVariable;


public static <T> Class<T> getGenericClass()
{
    __<T> ins = new __<T>();
    TypeVariable<?>[] cls = ins.getClass().getTypeParameters(); 

    return (Class<T>)cls[0].getClass();
}

private final class __<T> // generic helper class which does only provide type information
{
    private __()
    {
    }
}

Tuve este problema en una clase genérica abstracta. En este caso particular, la solución es más simple:

abstract class Foo<T> {
    abstract Class<T> getTClass();
    //...
}

y más tarde en la clase derivada:

class Bar extends Foo<Whatever> {
    @Override
    Class<T> getTClass() {
        return Whatever.class;
    }
}

Una ruta mejor que la Clase que los otros sugirieron es pasar un objeto que pueda hacer lo que habría hecho con la Clase, por ejemplo, crear una nueva instancia.

interface Factory<T> {
  T apply();
}

<T> void List<T> make10(Factory<T> factory) {
  List<T> result = new ArrayList<T>();
  for (int a = 0; a < 10; a++)
    result.add(factory.apply());
  return result;
}

class FooFactory<T> implements Factory<Foo<T>> {
  public Foo<T> apply() {
    return new Foo<T>();
  }
}

List<Foo<Integer>> foos = make10(new FooFactory<Integer>());

   public <T> T yourMethodSignature(Class<T> type) {

        // get some object and check the type match the given type
        Object result = ...            

        if (type.isAssignableFrom(result.getClass())) {
            return (T)result;
        } else {
            // handle the error
        }
   }




generics