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