¿Es posible obtener el tipo de un parámetro genérico?
Un ejemplo:
public final class Voodoo {
public static void chill(List<?> aListWithTypeSpiderMan) {
// Here I'd like to get the Class-Object 'SpiderMan'
Class typeOfTheList = ???;
}
public static void main(String... args) {
chill(new ArrayList<SpiderMan>());
}
}
java
generics
reflection
cimnine
fuente
fuente
Exception in thread "main" java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
no estoy seguro de cuál es la restricción.java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
Quiero intentar desglosar la respuesta de @DerMike para explicar:
Primero, la eliminación de tipo no significa que el JDK elimine la información de tipo en tiempo de ejecución. Es un método para permitir que la verificación de tipos en tiempo de compilación y la compatibilidad de tipos en tiempo de ejecución coexistan en el mismo idioma. Como implica este bloque de código, el JDK retiene la información del tipo borrado, simplemente no está asociado con los moldes y demás cosas verificadas.
En segundo lugar, esto proporciona información de tipo genérico a una clase genérica exactamente un nivel por encima de la jerarquía del tipo concreto que se verifica, es decir, una clase padre abstracta con parámetros de tipo genérico puede encontrar los tipos concretos correspondientes a sus parámetros de tipo para una implementación concreta de sí misma que hereda directamente de eso. Si esta clase no fuera abstracta e instanciada, o la implementación concreta se redujera dos niveles, esto no funcionaría (aunque un poco de fluctuación podría hacer que se aplique a cualquier número predeterminado de niveles más allá de uno, o hasta la clase más baja con X parámetros de tipo genérico, etcétera).
De todos modos, a la explicación. Aquí está el código nuevamente, separado en líneas para facilitar la referencia:
Permítanos 'ser' la clase abstracta con tipos genéricos que contiene este código. Leyendo esto más o menos al revés:
... y eso es todo. Por lo tanto, enviamos información de tipo de nuestra propia implementación concreta a nosotros mismos y la usamos para acceder a un identificador de clase. podríamos doblar getGenericSuperclass () e ir a dos niveles, o eliminar getGenericSuperclass () y obtener valores para nosotros como un tipo concreto (advertencia: no he probado estos escenarios, todavía no se me han ocurrido).
Se vuelve complicado si tus hijos concretos están a una distancia arbitraria de saltos, o si eres concreto y no final, y especialmente complicado si esperas que alguno de tus hijos (variablemente profundos) tenga sus propios genéricos. Pero, por lo general, puede diseñar en torno a esas consideraciones, por lo que esto le ayuda en la mayor parte del camino.
¡Espero que esto haya ayudado a alguien! Reconozco que esta publicación es antigua. Probablemente recortaré esta explicación y la guardaré para otras preguntas.
fuente
En realidad, tengo esto para trabajar. Considere el siguiente fragmento:
Estoy usando jdk 1.6
fuente
Hay una solución en realidad, aplicando el truco de "clase anónima" y las ideas de los Super Type Tokens :
El truco radica en la creación de una clase anónima ,
new ArrayList<SpiderMan>() {}
en el lugar del original (simple)new ArrayList<SpiderMan>()
. El uso de una clase anónima (si es posible) asegura que el compilador retiene información sobre el argumento de tipoSpiderMan
dado al parámetro de tipoList<?>
. Voilà!fuente
Debido a la eliminación de tipos, la única forma de conocer el tipo de la lista sería pasar el tipo como parámetro al método:
fuente
Apéndice a la respuesta de @ DerMike para obtener el parámetro genérico de una interfaz parametrizada (usando el método #getGenericInterfaces () dentro de un método predeterminado Java-8 para evitar la duplicación):
fuente
Awesomeable<Beer>
. En ese caso, se conserva la información de tipo. Si pasanew Awesomable<Beer> ()
al método wt no funcionará.Awesomable<Beer>
sobre la marcha sin la definición explícita de una subclase concreta comoEstrellaGalicia
en este caso, todavía está obteniendo el tipo parametrizado: lo ejecuté ahora:System.out.println("Type is: " + new Awesomable<Beer>() {}.parameterizedType());
---> El tipo es: ParameterizedStuff $ BeerNo, eso no es posible. Debido a problemas de compatibilidad con versiones anteriores, los genéricos de Java se basan en el borrado de tipos , es decir, en tiempo de ejecución, todo lo que tiene es un
List
objeto no genérico . Hay información sobre los parámetros de tipo en tiempo de ejecución, pero reside en las definiciones de clase (es decir, puede preguntar " ¿qué tipo genérico utiliza la definición de este campo? "), No en instancias de objetos.fuente
Como señaló @bertolami, no es posible para nosotros un tipo de variable y obtener su valor futuro (el contenido de la variable typeOfList).
Sin embargo, puede pasar la clase como parámetro de esta manera:
Eso es más o menos lo que hace Google cuando tiene que pasar una variable de clase al constructor de ActivityInstrumentationTestCase2 .
fuente
No, no es posible.
Puede obtener un tipo genérico de un campo dado que una clase es la única excepción a esa regla e incluso eso es un truco.
Consulte Conocer el tipo de genérico en Java para ver un ejemplo de eso.
fuente
Puede obtener el tipo de un parámetro genérico con reflexión como en este ejemplo que encontré aquí :
fuente
La respuesta rápida a la pregunta es no, no se puede, debido a la eliminación de tipo genérico de Java.
La respuesta más larga sería que si ha creado su lista de esta manera:
Entonces, en este caso, el tipo genérico se conserva en la superclase genérica de la nueva clase anónima anterior.
No es que recomiendo hacer esto con listas, pero es una implementación de escucha:
Y dado que extrapolar los tipos genéricos de superclases y super interfaces cambian entre JVM, la solución genérica no es tan sencilla como algunas respuestas podrían sugerir.
Aquí está ahora lo hice.
fuente
Esto es imposible porque los genéricos en Java solo se consideran en tiempo de compilación. Por lo tanto, los genéricos de Java son solo una especie de preprocesador. Sin embargo, puede obtener la clase real de los miembros de la lista.
fuente
Lo he codificado para métodos que esperan aceptar o devolver
Iterable<?...>
. Aquí está el código:fuente
No puede obtener un parámetro genérico de una variable. Pero puede desde un método o declaración de campo:
fuente
Solo para mí, leer este fragmento de código fue difícil, simplemente lo dividí en 2 líneas legibles:
Quería crear una instancia del parámetro Type sin tener ningún parámetro para mi método:
fuente
Aquí hay otro truco. Use una matriz genérica de vararg
fuente
Noté que muchas personas se inclinan hacia la
getGenericSuperclass()
solución:Sin embargo, esta solución es propensa a errores. Será no funciona correctamente si hay genéricos en los descendientes. Considera esto:
¿Qué tipo tendrá
Bar.persistentClass
?Class<Integer>
? No, lo seráClass<Double>
. Esto sucederá debido a quegetClass()
siempre devuelve la clase más alta, que esBar
en este caso, y su superclase genérica esFoo<Double>
. Por lo tanto, el tipo de argumento seráDouble
.Si necesita una solución confiable que no falle, puedo sugerir dos.
Guava
. Tiene una clase que se hizo precisamente para este propósito:com.google.common.reflect.TypeToken
. Maneja todos los casos de esquina muy bien y ofrece una funcionalidad más agradable. La desventaja es una dependencia extra. Dado que ha usado esta clase, su código se vería simple y claro, así:fuente
Utilizar:
fuente
toArray()
vuelveObject[]
. No puede y no debe depender de que sea un subtipo particular.