Obtener el tipo de un parámetro genérico en Java con reflexión

Resuelto cimnine asked hace 14 años • 18 respuestas

¿Es posible obtener el tipo de 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>());
    }
}
cimnine avatar Dec 14 '09 21:12 cimnine
Aceptado

Una construcción con la que me topé una vez parecía

Class<T> persistentClass = (Class<T>)
   ((ParameterizedType)getClass().getGenericSuperclass())
      .getActualTypeArguments()[0];

Entonces parece haber algo de magia de reflexión alrededor que desafortunadamente no entiendo del todo... Lo siento.

DerMike avatar Dec 14 '2009 14:12 DerMike

Quiero intentar desglosar la respuesta de @DerMike para explicar:

Primero, el borrado de tipos no significa que el JDK elimine la información de tipos 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 lenguaje. Como lo implica este bloque de código, el JDK retiene la información de tipo borrada; simplemente no está asociada con conversiones marcadas y demás.

En segundo lugar, esto proporciona información de tipo genérico a una clase genérica exactamente a un nivel superior en la jerarquía del tipo concreto que se está verificando; es decir, una clase principal 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 él. Si esta clase no fuera abstracta y estuviera instanciada, o la implementación concreta estuviera dos niveles por debajo, esto no funcionaría (aunque un poco de manipulació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, vamos a la explicación. Aquí está el código nuevamente, separado en líneas para facilitar la referencia:

1# Clase genericParameter0OfThisClass =
2# (Clase)
3# ((Tipo parametrizado)
4# obtenerClase()
5# .getGenericSuperclass())
6# .getActualTypeArguments()[0];

Seamos 'nosotros' la clase abstracta con tipos genéricos que contiene este código. Leyendo esto aproximadamente al revés:

  • La línea 4 obtiene la instancia de clase de la clase concreta actual. Esto identifica el tipo concreto de nuestro descendiente inmediato.
  • La línea 5 obtiene el supertipo de esa clase como Tipo; somos nosotros. Dado que somos un tipo paramétrico, podemos convertirnos con seguridad en ParameterizedType (línea 3). La clave es que cuando Java determina este objeto Tipo, utiliza la información de tipo presente en el hijo para asociar la información de tipo con nuestros parámetros de tipo en la nueva instancia de ParameterizedType. Ahora podemos acceder a tipos concretos para nuestros genéricos.
  • La línea 6 obtiene la matriz de tipos asignados a nuestros genéricos, en el orden declarado en el código de clase. Para este ejemplo sacamos el primer parámetro. Esto regresa como un Tipo.
  • La línea 2 arroja el Tipo final devuelto a una Clase. Esto es seguro porque sabemos qué tipos pueden tomar nuestros parámetros de tipo genérico y podemos confirmar que todos serán clases (no estoy seguro de cómo en Java se podría obtener un parámetro genérico que no tenga una instancia de clase). asociado con él, en realidad).

...y eso es prácticamente todo. Entonces, ingresamos la información de tipo de nuestra propia implementación concreta y la usamos para acceder a un identificador de clase. podríamos duplicar getGenericSuperclass() e ir dos niveles, o eliminar getGenericSuperclass() y obtener valores para nosotros mismos como un tipo concreto (advertencia: no he probado estos escenarios, aún no me han surgido).

Se vuelve complicado si sus hijos concretos están a un número arbitrario de saltos de distancia, o si usted es concreto y no definitivo, y especialmente complicado si espera que alguno de sus hijos (variablemente profundos) tenga sus propios genéricos. Pero normalmente puedes diseñar teniendo en cuenta esas consideraciones, por lo que esto te ayudará 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.

Mark McKenna avatar Dec 20 '2012 14:12 Mark McKenna