objeto - Genéricos de Java: Accediendo al Tipo Genérico en tiempo de ejecución
tipo comodin java (4)
Estoy buscando acceder al tipo genérico de un campo declarado durante el tiempo de ejecución. Antes tenía la impresión de que esto no era posible debido a la eliminación del tipo de Java. Sin embargo, este no debe ser el caso porque algunos marcos bien conocidos aprovechan el tipo genérico a través de la reflexión durante el tiempo de ejecución.
Como ejemplo, Guice implementará un proveedor basado en el tipo genérico que proporcione:
public class Injectable{
@Inject
private Provider<SomeType> someTypeProvider;
}
¿Cómo se accede al atributo genérico ''SomeType'' de un campo o cualquier tipo / método / etc. a través de la API de reflexión?
Además, sería útil saber cómo acceder a estos atributos de tipo genérico a través de la API del procesador de anotaciones Java 6.
Gracias.
Editar:
Gracias a todos por sus buenos consejos. Encontré una manera de hacer esto usando los enlaces de Haylem, específicamente el del artículo de Prenkov Reflexión de Java: Genéricos .
Aquí está la respuesta que estaba buscando:
/**
* @author John Ericksen
*/
public class TypeReflectionExample {
public class SomeType{}
public class Injectable{
@Inject private Provider<SomeType> someTypeProvider;
}
public static void main(String[] args){
try {
Field providerField = Injectable.class.getDeclaredField("someTypeProvider");
Type genericFieldType = providerField.getGenericType();
if(genericFieldType instanceof ParameterizedType){
ParameterizedType aType = (ParameterizedType) genericFieldType;
Type[] fieldArgTypes = aType.getActualTypeArguments();
for(Type fieldArgType : fieldArgTypes){
Class fieldArgClass = (Class) fieldArgType;
System.out.println("fieldArgClass = " + fieldArgClass);
}
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
resultados en:
fieldArgClass = class test.TypeReflectionExample $ SomeType
Lo mismo se puede hacer para Métodos, Constructores, extensiones de Superclase / implementos, etc.
Estoy concediendo Haylem, ya que su publicación me llevó a esta solución, incluso si no respondía directamente a mi pregunta.
Bueno, ¿por qué no miras lo que hace Guice ? La fuente está disponible públicamente.
Guice hace estas cosas en múltiples niveles. Uno que sobresale en particular son los literales de tipo .
El punto clave aquí es que mientras los tipos se compilan usando borrado de tipo (por lo que solo hay una clase para cada tipo), todavía existen múltiples objetos de Type
, que sí conocen los genéricos utilizados. Sin embargo, el código se optimiza independientemente de eso (ya que fue compilado por clase, y no por tipo).
Eche un vistazo a la API de Java de ParameterizedType
.
Entonces, si bien es cierto que Java Generics se implementan mediante "borrado de tipo" en un nivel de clase , esto no se mantiene completamente en un nivel de Type
. Desafortunadamente, manejar Tipos es mucho más complicado que las clases. Además, esto también implica que Java no puede optimizar los genéricos de la misma manera que lo hace C ++, en particular para los tipos primitivos. Una ArrayList<Integer>
será, por diseño, una ArrayList<?>
contenga objetos, y no respaldada por una matriz int[]
nativa cuando sea posible.
Tenga en cuenta que esto es, sin embargo, bastante cerca de hacer un seguimiento de estas cosas usted mismo. Digamos, muy ingenuamente (no funcionará con genéricos anidados), podría extender ArrayList<T>
con una clase, que tiene un campo Class<T> contentClass
, entonces podrá encontrar esta información en tiempo de ejecución. (¡Sin embargo, un TypeLiteral puede ser la mejor opción en lugar de una Class aquí!) Además, el JRE en realidad no se asegurará de que la lista permanezca constante. Al igual que podría lanzar un ArrayList<Integer>
en un ArrayList
tipo y agregar un objeto String
.
Creo que Guice usa TypeLiteral para encapsular información genérica en un objeto separado.
Es cierto que los genéricos no son generalmente conocidos en tiempo de ejecución en Java, porque se implementan con Type Erasure.
Reflejando genéricos?
Sin embargo, aún puede extraer alguna información valiosa sobre los tipos declarados (NO los tipos de objetos en tiempo de ejecución), como se presenta en el artículo de Ian Roberston Reflecting Generics y el artículo de Prenkov Reflexión en Java: Generics .
Antecedentes sobre genéricos y eliminación de tipos
Los genéricos se introdujeron conservando la compatibilidad con versiones anteriores en el nivel de fuente y binario, por lo tanto, parte de su limitación, como:
- la imposibilidad de tener una forma abreviada sin al menos algún indicador de soporte de genéricos (aquí, el llamado operador de diamantes
<>
), - la imposibilidad de inspeccionar los tipos genéricos en tiempo de ejecución, porque tenían que implementarse con Type Erasure .
Otras lecturas
- De The Java Tutorial :
- sección sobre Tipos Genéricos
- sección sobre Inferencia de tipos y creación de instancias de clases genéricas
- De las especificaciones de lenguaje Java (JLS):
- Sección JLS de Java SE 5 sobre tipos, valores y variables
- Sección JLS de Java SE 7 sobre tipos, valores y variables
- De buenas preguntas sobre :
- Otros:
- IBM Developer Series: teoría y práctica de Java: genéricos Gotchas (especialmente las secciones The Road Not Taken , Generifying Existing Classes e Implications of Erasure ).
Lo he usado hace un año o así. Podría ayudarte
Type typeOfSrc = type(YourClass.class, clazz);
// type(C, A1,...,An) => C<A1,...,An>
static ParameterizedType type(final Class raw, final Type... args)
{
return new ParameterizedType()
{
public Type getRawType(){ return raw; }
public Type[] getActualTypeArguments(){ return args; }
public Type getOwnerType(){ return null; }
};
}
Esto le permite acceder al tipo genérico en tiempo de ejecución. Básicamente, lo que estás haciendo aquí es almacenar la información genérica en otra clase y usar esa clase para recuperar esa información en tiempo de ejecución.