cannot - Lista de genéricos de casting en Java.
java casting (5)
Encontré una situación extraña al lanzar genéricos. Ejecuto este código:
class A { }
class B { }
public class Program {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
List<A> listA = new ArrayList<A>();
List<?> list = listA;
((List<B>)list).add(new B());
for (Object item : listA) {
System.out.println(item.toString());
}
}
}
Se compila muy bien (solo con advertencia pero sin error) y se ejecuta sin ninguna excepción y la salida fue:
B @ 88140ed
¿Cómo hice eso? Quiero decir por qué Java me permite hacer tal cosa? He añadido una instancia de la clase B
a la lista de A
s?
Es muy mal comportamiento de los genéricos. ¿Por qué está sucediendo?
Por cierto, lo probé con Java 7.
EDITAR:
Lo que me sorprendió es que Java solo notifica el problema con una advertencia de que cada programador puede ignorarlo. Sé que SuppressWarnings
es una mala idea, pero ¿por qué Java no negó tal comportamiento con error o excepción?
Además, esta advertencia se mostró siempre, si crees que tu lanzamiento es correcto, no tienes más remedio que ignorarlo. Pero si crees que es un buen casting y lo ignoras pero no lo es?
Es muy mal comportamiento de los genéricos. ¿Por qué está sucediendo?
Porque forzó que sucediera con el reparto, y luego se aseguró de que las advertencias se ignoraran también con sus @SuppressWarnings("unchecked")
.
Es tu culpa, no de los mecanismos genéricos.
Cada lenguaje de programación te permite dispararte a ti mismo en el pie .
En este caso, Java se encuentra en un dilema: podría mantener la información genérica en el código de bytes y romper millones de líneas de código existente o eliminar silenciosamente la información genérica después de que el compilador haya hecho todo lo posible por verificar y mantener la compatibilidad con versiones anteriores.
El equipo de Java decidió por este último e introdujo Type Erasure, que tiene sus defectos . Pero si hubieran roto millones de líneas de código Java perfectamente finas (si no las hubieran tipificado), la gente habría aparecido con horcas y antorchas encendidas ...
Como otros han dicho, usted ha eludido la seguridad de tipo java.
Agregaré que su código no explota porque su código no requiere que los elementos sean nada en particular (solo Objeto). Sin embargo, si hubieras codificado esto:
for (A item : listA) { /* whatever */ }
Se habría compilado, pero se habría lanzado una ClassCastException en tiempo de ejecución.
Ha derrotado los controles de tiempo de compilación de Java a través de su lanzamiento y supresión de advertencias.
Tenga en cuenta que gracias a type-erasure la lista que ha creado es (debajo de las portadas) una lista simple que no conoce el tipo, y no contiene verificaciones en tiempo de ejecución ni aseveraciones sobre lo que está agregando.
Puedes modificar el código para que sea más seguro:
for (A item : listA) {/* your code here */}
O
for (Object item : listA) {
if (item instanceof A) {for (A item : listA) {/* your code here*/}
}
Incluso si modifica el código como se indica a continuación, obtendrá la excepción ClassCastException:
for (Object item : listA) {
System.out.println(((A)item).toString()); // Here you will get ClassCastException
}