Java: ¿cómo obtengo una clase literal de un tipo genérico?
Normalmente, he visto a personas usar la clase literal de esta manera:
Class<Foo> cls = Foo.class;
Pero ¿qué pasa si el tipo es genérico, por ejemplo Lista? Esto funciona bien, pero tiene una advertencia ya que la Lista debe estar parametrizada:
Class<List> cls = List.class
Entonces, ¿por qué no agregar un <?>
? Bueno, esto provoca un error de no coincidencia de tipos:
Class<List<?>> cls = List.class
Pensé que algo como esto funcionaría, pero esto es solo un simple error de sintaxis:
Class<List<Foo>> cls = List<Foo>.class
¿Cómo puedo obtenerlo de Class<List<Foo>>
forma estática, por ejemplo, utilizando la clase literal?
Podría utilizar@SuppressWarnings("unchecked")
para deshacerme de las advertencias causadas por el uso no parametrizado de List en el primer ejemplo, pero Class<List> cls = List.class
prefiero no hacerlo.
¿Alguna sugerencia?
No puedes debido al borrado de tipo .
Los genéricos de Java son poco más que azúcar sintáctico para conversiones de objetos. Demostrar:
List<Integer> list1 = new ArrayList<Integer>();
List<String> list2 = (List<String>)list1;
list2.add("foo"); // perfectly legal
El único caso en el que se retiene información de tipo genérico en tiempo de ejecución es Field.getGenericType()
cuando se interroga a los miembros de una clase mediante reflexión.
Todo esto por eso Object.getClass()
tiene esta firma:
public final native Class<?> getClass();
La parte importante es Class<?>
.
Para decirlo de otra manera, de las preguntas frecuentes sobre genéricos de Java :
¿Por qué no existe una clase literal para tipos parametrizados concretos?
Porque el tipo parametrizado no tiene una representación exacta del tipo de tiempo de ejecución.
Un literal de clase denota un
Class
objeto que representa un tipo determinado. Por ejemplo, la clase literalString.class
denota elClass
objeto que representa el tipoString
y es idéntico alClass
objeto que se devuelve cuandogetClass
se invoca un método en unString
objeto. Se puede utilizar un literal de clase para comprobaciones de tipos en tiempo de ejecución y para reflexión.Los tipos parametrizados pierden sus argumentos de tipo cuando se traducen a código de bytes durante la compilación en un proceso llamado borrado de tipo. Como efecto secundario del borrado de tipos, todas las instancias de un tipo genérico comparten la misma representación en tiempo de ejecución, es decir, la del tipo sin formato correspondiente. En otras palabras, los tipos parametrizados no tienen representación de tipo propia. En consecuencia, no tiene sentido formar literales de clase como
List<String>.class
,List<Long>.class
yList<?>.class
, ya que talesClass
objetos no existen. Sólo el tipo sin formatoList
tiene unClass
objeto que representa su tipo de tiempo de ejecución. Se le conoce comoList.class
.
No existen literales de clase para tipos parametrizados; sin embargo, existen objetos de tipo que definen correctamente estos tipos.
Consulte java.lang.reflect.ParameterizedType - http://java.sun.com/j2se/1.5.0/docs/api/java/lang/reflect/ParameterizedType.html
La biblioteca Gson de Google define una clase TypeToken que permite generar simplemente tipos parametrizados y la utiliza para especificar objetos json con tipos parametrizados complejos de una manera genérica y amigable. En tu ejemplo usarías:
Type typeOfListOfFoo = new TypeToken<List<Foo>>(){}.getType()
Tenía la intención de publicar enlaces al javadoc de las clases TypeToken y Gson, pero Stack Overflow no me permite publicar más de un enlace porque soy un usuario nuevo, puedes encontrarlos fácilmente usando la búsqueda de Google.
Puedes gestionarlo con un doble reparto:
@SuppressWarnings("unchecked")
Class<List<Foo>> cls = (Class<List<Foo>>)(Object)List.class
Para ampliar la respuesta de Cletus, en tiempo de ejecución se eliminan todos los registros de los tipos genéricos. Los genéricos se procesan únicamente en el compilador y se utilizan para proporcionar seguridad de tipos adicional. En realidad, son solo una abreviatura que permite al compilador insertar encasillamientos en los lugares apropiados. Por ejemplo, anteriormente tendrías que hacer lo siguiente:
List x = new ArrayList();
x.add(new SomeClass());
Iterator i = x.iterator();
SomeClass z = (SomeClass) i.next();
se convierte
List<SomeClass> x = new ArrayList<SomeClass>();
x.add(new SomeClass());
Iterator<SomeClass> i = x.iterator();
SomeClass z = i.next();
Esto permite que el compilador verifique su código en tiempo de compilación, pero en tiempo de ejecución todavía se parece al primer ejemplo.