una - t generic java
¿Por qué no puedo lanzar una Colección<GenericFoo> a una Colección<GenericFoo<?>> (1)
En primer lugar, el tipo raw y el tipo de comodín son bastante diferentes. Por un lado, el tipo raw borra completamente toda la información genérica.
Entonces tenemos List<x>
y List<y>
donde x no es y. Esto ciertamente no es una relación de subtipo.
Sin embargo, puedes pedir que se permita el casting. Pero lea JLS 5.5.1 y dígame que desea agregarle algo más :) Navegue por toda la página, en realidad, es un gran muro de texto solo para el casting .
Y recuerda que esta es solo la primera onda en todo el efecto. ¿Qué pasa con la List<List<x>>
y la List<List<y>>
, etc.
El quid de la pregunta es, ¿por qué causa esto un error en tiempo de compilación?
List<Collection> raws = new ArrayList<Collection>();
List<Collection<?>> c = raws; // error
Fondo
Entiendo por qué los genéricos no son covariantes en general. Si pudiéramos asignar List<Integer>
a List<Number>
, nos expondríamos a ClassCastExceptions:
List<Integer> ints = new ArrayList<Integer>();
List<Number> nums = ints; // compile-time error
nums.add(Double.valueOf(1.2));
Integer i = ints.get(0); // ClassCastException
Recibimos un error en tiempo de compilación en la línea 2 para evitarnos un error en tiempo de ejecución en la línea 4. Eso tiene sentido.
List<C>
para List<C<?>>
Pero ¿qué tal esto?
List<Collection> rawLists = new ArrayList<Collection>();
List<Collection<?>> wildLists = rawLists; // compile-time error
// scenario 1: add to raw and get from wild
rawLists.add(new ArrayList<Integer>());
Collection<?> c1 = wildLists.get(0);
Object o1 = c1.iterator().next();
// scenario 2: add to wild and get from raw
wildLists.add(new ArrayList<String>());
Collection c2 = rawLists.get(0);
Object o2 = c2.iterator().next();
En ambos escenarios, en última instancia, solo obtengo elementos de Object
sin lanzar, por lo que no puedo obtener una ClassCastException "misteriosa".
La sección en el JLS que corresponde a esto es §4.10.2 , así que entiendo por qué el compilador me está dando el error; lo que no entiendo es por qué la especificación se escribió de esta manera, y (para evitar las respuestas especulativas / basadas en la opinión), si realmente me proporciona alguna seguridad en tiempo de compilación.
Ejemplo motivador
En caso de que se lo pregunte, aquí está (una versión simplificada de) el caso de uso:
public Collection<T> readJsons(List<String> jsons, Class<T> clazz) {
List<T> list = new ArrayList<T>();
for (String json : jsons) {
T elem = jsonMapper.readAs(json, clazz);
list.add(elem);
}
return list;
}
// call site
List<GenericFoo<?>> foos = readJsons(GenericFoo.class); // error
El error se debe a que GenericFoo.class
tiene el tipo Class<GenericFoo>
, no la Class<GenericFoo<?>>
( §15.8.2 ). No estoy seguro de por qué es eso, aunque sospecho que es una razón relacionada; pero a pesar de eso, eso no sería un problema si la Class<GenericFoo>
pudiera Class<GenericFoo>
, ya sea implícita o explícitamente, a la Class<GenericFoo<?>>
.