Java: ¿cómo obtengo una clase literal de un tipo genérico?

Resuelto Tom asked hace 14 años • 9 respuestas

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.classprefiero no hacerlo.

¿Alguna sugerencia?

Tom avatar Mar 06 '10 06:03 Tom
Aceptado

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 literal String.classdenota el Class objeto que representa el tipo Stringy es idéntico al Classobjeto que se devuelve cuando getClassse invoca un método en un Stringobjeto. 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>.classy List<?>.class , ya que tales Classobjetos no existen. Sólo el tipo sin formato Listtiene un Class objeto que representa su tipo de tiempo de ejecución. Se le conoce como List.class.

cletus avatar Mar 05 '2010 23:03 cletus

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.

Santi P. avatar Mar 08 '2010 04:03 Santi P.

Puedes gestionarlo con un doble reparto:

@SuppressWarnings("unchecked") Class<List<Foo>> cls = (Class<List<Foo>>)(Object)List.class

slaurent avatar Jun 10 '2015 11:06 slaurent

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.

Jim Garrison avatar Mar 05 '2010 23:03 Jim Garrison