java - descargar - ¿Por qué el uso de Collection<String>.class es ilegal?
java offline (3)
Estoy de acuerdo con las otras respuestas, y me gustaría explicar un punto más:
Los objetos de clase representan clases que se cargan en la memoria de JVM. Cada objeto de clase es en realidad una instancia en memoria de un archivo .class
. Los genéricos de Java no son clases separadas. Son solo una parte del mecanismo de verificación de tipos en tiempo de compilación. Por lo tanto, no tienen representación en tiempo de ejecución en un objeto de clase.
Estoy confundido por los genéricos. Puede declarar un campo como:
Class<Collection<String>> clazz = ...
Parece lógico que pueda asignar este campo con:
Class<Collection<String>> clazz = Collection<String>.class;
Sin embargo, esto genera un error:
Error de sintaxis en token ">", vacío esperado después de este token
Entonces parece que el operador .class
no funciona con genéricos. Así que lo intenté:
class A<S> { }
class B extends A<String> { }
Class<A<String>> c = B.class;
Además, no funciona, genera:
Tipo no coincidente: no se puede convertir de
Class<Test.StringCollection> to Class<Collection<String>>
Ahora, realmente no veo por qué esto no debería funcionar. Sé que los tipos genéricos no están reificados, pero en ambos casos parece ser completamente seguro sin tener acceso a los tipos genéricos de tiempo de ejecución. ¿Alguien una idea?
Los genéricos son invariables.
Object o = "someString"; // FINE!
Class<Object> klazz = String.class; // DOESN''T COMPILE!
// cannot convert from Class<String> to Class<Object>
Dependiendo de lo que necesite, puede usar comodines.
Class<? extends Number> klazz = Integer.class; // FINE!
O tal vez necesitas algo como esto:
Class<List<String>> klazz =
(Class<List<String>>) new ArrayList<String>().getClass();
// WARNING! Type safety: Unchecked cast from
// Class<capture#1-of ? extends ArrayList> to Class<List<String>>
En cuanto al caso no reificado en tiempo de ejecución, parece que tiene una buena comprensión, pero aquí hay una cita de todos modos, de los Tutoriales de Java sobre Genéricos , The Fine Print : Una Clase Genérica es Compartida por Todas Sus Invocaciones :
¿Qué imprime el siguiente fragmento de código?
List <String> l1 = new ArrayList<String>(); List<Integer> l2 = new ArrayList<Integer>(); System.out.println(l1.getClass() == l2.getClass());
Tal vez tengas la tentación de decir que es
false
, pero estarías equivocado. Imprimetrue
, porque todas las instancias de una clase genérica tienen la misma clase de tiempo de ejecución, independientemente de sus parámetros de tipo reales.
Es decir, no hay tal cosa como List<String>.class
o List<Integer>.class
; solo hay List.class
.
Esto también se refleja en los literales de clase JLS 15.8.2
Un literal de clase es una expresión que consiste en el nombre de una clase, interfaz, matriz o tipo primitivo, o el vacío de pseudo tipo, seguido de a
.
y laclass
token.
Tenga en cuenta la omisión de cualquier concesión para los parámetros / argumentos de tipo genérico. Además,
Es un error de tiempo de compilación si ocurre cualquiera de los siguientes:
- El tipo nombrado es una variable de tipo o un tipo parametrizado, o una matriz cuyo tipo de elemento es una variable de tipo o un tipo parametrizado.
Es decir, esto tampoco compila:
void <T> test() {
Class<?> klazz = T.class; // DOESN''T COMPILE!
// Illegal class literal for the type parameter T
}
Básicamente no puedes usar genéricos con literales de clase, porque simplemente no tiene sentido: no están reificados.
Parece que hay una falta en los literales de clase en Java, no hay forma de crear literales de clase con información genérica, aunque esto puede ser útil en ciertos casos. Por lo tanto, no se puede llamar al siguiente código porque es imposible proporcionar literalmente la clase
class A<S> {}
<S> A<S> foo( Class<A<S>> clazz ) {}
A<String> a = foo( A<String>.class ) // error
Sin embargo, mi principal problema era que tampoco podía llamarlo con una clase B que se extendía A. Esto fue causado por las restricciones de invarianza. Esto fue resuelto usando un comodín:
class A<S> {}
class B extends A<String> {}
<S> A<S> foo( Class<? extends A<S>> clazz ) { return null; }
void test () {
A<String> s = foo( B.class );
}
Dicho esto, no encontré una razón por la cual la razón subyacente es que la Class<A<S>>.class
es válida. Ni el borrado ni los límites parecen requerir que esto no sea válido.