java generics types type-erasure

Cómo obtener la clase de tipo variable en Java Generics



types type-erasure (7)

Hay una manera de obtener el tipo de tiempo de ejecución del parámetro de tipo utilizando el TypeToken de Guava para capturarlo. La desventaja de la solución es que debe crear una subclase anónima cada vez que necesite una instancia de Container .

class Container<T> { TypeToken<T> tokenOfContainedType = new TypeToken<T>(getClass()) {}; public Type getContainedType() { return tokenOfContainedType.getType(); } } class TestCase { // note that containerTest is not a simple instance of Container, // an anonymous subclass is created private Container<String> containerTest = new Container<String>() {}; @Test public void test() { Assert.assertEquals(String.class, containerTest.getContainedType()); } }

La clave de esta solución se describe en el TypeToken del constructor de TypeToken utilizado en el código anterior:

Los clientes crean una subclase anónima vacía. Al hacerlo, se incrusta el parámetro de tipo en la jerarquía de tipos de la clase anónima para que podamos reconstituirlo en el tiempo de ejecución a pesar del borrado.

He visto preguntas similares pero no ayudaron mucho.

Por ejemplo, tengo esta clase genérica:

public class ContainerTest<T> { public void doSomething() { //I want here to determinate the Class of the type argument (In this case String) } }

y otra clase que usa esta clase de contenedor

public class TestCase { private ContainerTest<String> containerTest; public void someMethod() { containerTest.doSomething(); } }

¿Es posible determinar la Clase del argumento de tipo en el método doSomething () sin tener una variable / campo de tipo explícito o cualquier constructor en la Clase ContainerTest?

Actualización: formato modificado de la clase ContainerTest


La única forma es almacenar la clase en una variable de instancia y exigirla como un argumento del constructor:

public class ContainerTest<T> { private Class<T> tClass; public ContainerTest(Class<T> tClass) { this.tCLass = tClass; } public void doSomething() { //access tClass here } }


No hay una forma "limpia" de obtener el argumento Tipo genérico dentro de la clase. En su lugar, un patrón común es pasar la Clase del tipo genérico al constructor y mantenerlo como una propiedad interna como se hace en la implementación java.util.EnumMap.

http://docs.oracle.com/javase/1.5.0/docs/api/java/util/EnumMap.html http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/EnumMap.java

public class ContainerTest<T> { Class<T> type; T t; public ContainerTest(Class<T> type) { this.type = type; } public void setT(T t) { this.t = t; } public T getT() { return t; } public void doSomething() { //There you can use "type" property. } }


No. No es posible debido al borrado de tipo (los parámetros de tipo se compilan como objetos de tipo Objeto +). Si realmente necesita saber / aplicar el tipo en tiempo de ejecución, puede almacenar una referencia a un objeto Class .

public class ContainerTest<T> { private final Class<T> klass; private final List<T> list = new ArrayList<T>(); ContainerTest(Class<T> klass) { this.klass = klass; } Class<T> getElementClass() { return klass; } void add(T t) { //klass.cast forces a runtime cast operation list.add(klass.cast(t)); } }

Utilizar:

ContainerTest<String> c = new ContainerTest<>(String.class);


Para conocer el valor de T , deberá capturarlo en una definición de tipo mediante la subclase de ContainerTest:

class MyStringClass extends ContainerTest<String> {}

También puedes hacer esto con una clase anónima si quieres:

ContainerTest<String> myStringClass = new ContainerTest<String>{}();

Luego, para resolver el valor de T, puedes usar TypeTools :

Class<?> stringType = TypeResolver.resolveRawArgument(ContainerTest.class, myStringClass.getClass()); assert stringType == String.class;


Si está interesado en la forma de reflexión , encontré una solución parcial en este gran artículo: http://www.artima.com/weblogs/viewpost.jsp?thread=208860

En resumen, puede usar los métodos java.lang.Class.getGenericSuperclass() y java.lang.reflect.ParameterizedType.getActualTypeArguments() , pero debe subclasificar alguna superclase padre.

El siguiente fragmento de código funciona para una clase que extiende directamente la superclase AbstractUserType . Vea el artículo de referencia para una solución más general.

import java.lang.reflect.ParameterizedType; public class AbstractUserType<T> { public Class<T> returnedClass() { ParameterizedType parameterizedType = (ParameterizedType) getClass() .getGenericSuperclass(); @SuppressWarnings("unchecked") Class<T> ret = (Class<T>) parameterizedType.getActualTypeArguments()[0]; return ret; } public static void main(String[] args) { AbstractUserType<String> myVar = new AbstractUserType<String>() {}; System.err.println(myVar.returnedClass()); } }


Si puedes definir así

public class ContainerTest<T> { public void doSomething(T clazz) { } }

Entonces es posible