2012-06-22 10 views
8

imaginar el siguiente escenario:Reflexión para la clase del parámetro genérico en Java?

class MyClass extends OtherClass<String>{ 

    String myName; 
    //Whatever 

} 

class OtherClass<T> { 

    T myfield; 

} 

Y estoy analizando MiClase utilizando la reflexión específicamente (MyClass.class).getDeclaredFields(), en este caso voy a conseguir los siguientes campos (y tipos, utilizando getType() del campo):

myName --> String 
myField --> T 

Quiero obtener el Tipo real para T, que se conoce en tiempo de ejecución debido a la explícita "Cadena" en la notación extends, ¿cómo hago para obtener el tipo no genético de myField?

EDITAR RESUELVE:

Parece que la respuesta es "no se puede". Para aquellos que puedan ver esta pregunta más tarde, les recomendaría usar Jackson (estaba intentando hacer esto para generar JSON) y anotar sus clases y campos de tal manera que Jackson esté al tanto de la jerarquía de herencia y pueda hacer automáticamente la respuesta correcta a continuación sugerida.

+1

La respuesta no es que no puedas; puede crear fácilmente un mapa desde los parámetros de tipo hasta los argumentos de tipo explícito. – Jeffrey

Respuesta

18

Esto se puede lograr con la reflexión solo porque usó explícitamente String; de lo contrario, esta información se habría perdido debido a borrado de tipo.

ParameterizedType t = (ParameterizedType) MyClass.class.getGenericSuperclass(); // OtherClass<String> 
Class<?> clazz = (Class<?>) t.getActualTypeArguments()[0]; // Class<String> 
+0

Parece que ganas la competencia por la respuesta completa :) – Jochen

+0

Esta es una gran respuesta; sin embargo, ¿cómo puedo hacer esto en general con un objeto Field? No quiero pedir explícitamente la súper clase porque solo estoy tratando de obtener todos los campos declarados de mi subclase, uno de los cuales es genérico. Solo quiero algo similar a getDeclaredFields(), pero más inteligente sobre los genéricos. ¿Tiene sentido? –

+0

@hatboysam De una forma u otra tienes que pedir la súper clase. Llamar a 'Field.getGenericParameter' solo devolverá el parámetro de tipo (en este caso' T'), no el argumento de tipo explícito ('String'). Puede tomar este parámetro de tipo y buscar el argumento de tipo explícito creando un mapa de 'MyClass.class.getSuperclass(). GetTypeArguments()' a 'MyClass.class.getGenericSuperclass(). GetActualTypeArguments()'. – Jeffrey

-4

No hay forma directa de obtener el tipo real debido al Type Erasure. Sin embargo, usted puede utilizar la siguiente manera:

en su OtherClass<T>, escribir el siguiente método abstracto:

protected abstract class<T> getClazz(); 

Luego, en MyClass, se implementa el método:

@Override 
protected Class<String> getClazz(){ 
    return String.class; 
} 

entonces usted puede llamar getClazz() para obtener la clase

+0

+1 para el enlace a Type Erasure – BlackVegetable

+2

El OP dio un parámetro de tipo concreto para su clase genérica, esa información * se * mantiene en tiempo de ejecución y se puede recuperar. – Jeffrey

+0

-1 - Esta solución no es necesaria como las otras indicadas en esta publicación.Además, su 'getClass()' tendría un choque de nombre con el método final ['Object # getClass()'] (http://docs.oracle.com/javase/7/docs/api/java/lang/Object .html # getClass \ (\)) –

0

Los tipos genéricos son no conocidos durante el tiempo de ejecución. Solo el compilador los conoce, verifica que su programa esté tipeado correctamente y luego los elimina.

En su caso particular, llamando al MyClass.class.getGenericSuperclass() puede darle la información que necesita, porque por alguna extraña razón, los tipos concretos utilizados al heredar se guardan en el descriptor de la clase.

+2

No es "una razón extraña". * Las declaraciones * de campos, métodos, superclases, clases adjuntas, etc. se conservan siempre, porque otras clases deben usarlas al compilar. Este es un problema separado de los tipos de objetos en tiempo de ejecución, donde no hay genéricos. – newacct

3

encontré una buena explicación here:

Cuando la inspección de un tiempo de ejecución en sí de tipo parametrizable, como java.util.List, no hay manera de saber qué tipo se ha parametrizado a. Esto tiene sentido ya que el tipo se puede parametrizar a todo tipo de tipos en la misma aplicación. Pero cuando inspecciona el método o campo que declara el uso de un tipo parametrizado, puede ver en tiempo de ejecución a qué tipo se ha parametrizado el tipo parametrizable.

En resumen:

No se puede ver en un mismo tipo de qué tipo es parametrizada a un tiempo de ejecución, pero se puede ver en los campos y métodos en los que se utiliza y con parámetros.

En código:

No se puede ver T aquí:

class MyClass<T> { T myField; } 

Se puede ver la "T" aquí:

class FooClass { 
    MyClass<? extends Serializable> fooField; 
} 

Aquí sería capaz de contar la escriba y escriba los parámetros de fooField. Vea getGeneric*() métodos de Class y Method.

Por cierto, a menudo veo esto (acortada):

Class fieldArgClass = (Class) aType.getActualTypeArguments()[0]; 

Esto no es correcto, porque getActualTypeArguments() puede, ya menudo, volver TypeVariable en lugar de clase - que es cuando el genérico es <? extends SomeClass> en lugar de solo <SomeClass>. Se puede ir más profundo, imaginar:

class FooClass { 
    MyClass<? extends Map<String, List<? extends Serializable>>> fooField; 
} 

para que pueda obtener un árbol de Type s. Pero eso es un poco fuera de tema. Disfrutar :)

0

Este es un ejemplo clásico de por qué la reflexión no es una gran idea.

Lo que puede obtener de un programa por reflexión son solo aquellos hechos que las personas compiladoras del idioma eligen poner a disposición.

Y generalmente no pueden permitirse el lujo de tener todo disponible; tendrían que mantener el texto en bruto del programa.

Por lo tanto, todos los demás datos sobre su código no están disponibles para el reflector.

La cura para esto es dar un paso fuera de el lenguaje y el uso de una herramienta que puede proporcionar cualquier bit arbitraria de información sobre el código. Tales herramientas se llaman Program Transformation Systems (PTS).

A PTS analiza el código fuente y crea un AST que lo representa. Un buen PTW construirá un AST que contenga esencialmente todo sobre el código (operadores, operandos, puntuación, comentarios) para que pueda ser inspeccionado. Normalmente, un PTS registrará la posición de línea/columna de los tokens de idioma para que la información de diseño esté disponible; PTS extremo registrará el espacio en blanco en los huecos entre tokens o al menos sabrá cómo leer el archivo de texto original cuando sea necesario si se le pregunta al respecto. Este AST es, en esencia, el equivalente al texto completo que dije que sería necesario, pero en una forma más conveniente de procesar.

(PTS tienen otra propiedad muy agradable: pueden modificar AST y regenerar el código para el programa modificado. Pero eso está por encima y más allá de la reflexión así que no comentaré más sobre este aspecto).

Cuestiones relacionadas