reconoce - La clase genérica se compila en Java 6, pero no en Java 7
compile java command line linux (2)
Tengo una interfaz en Java 6 que compila correctamente:
public interface IMultiMap<K, V> extends Map<K, Set<V>> {
public int valueSize();
public boolean put(K key, V value);
public void clear(Object key);
public boolean isEmpty(Object key);
}
Pero en Java 7, esta interfaz no compila. Recibo un error de compilación en el valor boolean put(K, V)
que tiene el mismo borrado que V put(K, V)
. El error completo del compilador:
error: name clash: put(K#1,V#1) in IMultiMap and put(K#2,V#2) in Map have the same erasure, yet neither overrides the other
public boolean put(K key, V value);
where K#1,V#1,K#2,V#2 are type-variables:
K#1 extends Object declared in interface IMultiMap
V#1 extends Object declared in interface IMultiMap
K#2 extends Object declared in interface Map
V#2 extends Object declared in interface Map
Para el registro, agregar cualquier tipo de anulación no funciona. He intentado anular explícitamente Map.put
, pero el error sigue apareciendo. Cambiar el tipo de retorno de mi put
es discutible ya que este error está bloqueando el error potencial que se puede alcanzar, y si este error se solucionara, los dos métodos no tendrían el mismo nombre / firma de parámetros de todos modos.
Creo que podría intentar reflexionar sobre Java 6 y ver cómo terminan los tipos de parámetros reales en el código de bytes compilado de Java 6. Está claro que ambos métodos de Java 7 se están borrando para put(Object, Object)
. Publicaré los resultados de la reflexión aquí una vez que lo haga.
Mientras tanto, mi solución temporal será simplemente cambiar el nombre de put
a putSingle
, pero ¿es correcto este nuevo comportamiento? ¿Cambió alguna parte de las especificaciones de genéricos para Java 7 que hace que el antiguo comportamiento de Java 6 sea incorrecto? ¿O es esto un error en el compilador de Java 7?
Gracias por adelantado.
EDITAR: Corrí el código de reflexión. Echa un vistazo a mi respuesta a continuación.
Creo que es un error en 1.6 que se corrigió en 1.7. Extracto de esta página :
Sinopsis: una clase no puede definir dos métodos con la misma firma borrada pero dos tipos de devolución diferentes
Descripción: una clase no puede definir dos métodos con la misma firma borrada, independientemente de si los tipos de devolución son iguales o no. Esto se deduce de JLS, Java SE 7 Edition, sección 8.4.8.3. El compilador JDK 6 permite métodos con la misma firma borrada pero diferentes tipos de retorno; este comportamiento es incorrecto y se ha corregido en JDK 7.
Ejemplo:
class A {
int m(List<String> ls) { return 0; }
long m(List<Integer> ls) { return 1; }
}
Este código se compila bajo JDK 5.0 y JDK 6, y se rechaza bajo JDK 7.
Ejecuté el código de reflexión en Java 6.
Aquí está el código:
public static void main(String[] args) {
Class<IMultiMap> immClass = IMultiMap.class;
Method[] methods = immClass.getMethods();
for (Method method : methods) {
if (method.getName().equals("put"))
System.out.println(method.toString());
}
}
Aquí están las firmas del método para la clase:
public abstract boolean IMultiMap.put(java.lang.Object,java.lang.Object)
public abstract java.lang.Object java.util.Map.put(java.lang.Object,java.lang.Object)
O más concisamente:
boolean put(Object, Object)
Object put(Object, Object)
Por lo tanto, se borran a los mismos parámetros con un tipo de retorno diferente. Supongo que es un error un caso de borde no especificado en Java 6 JLS, según la respuesta de Assylias. Me pregunto cómo Java 6 logró resolver estos métodos correctamente en tiempo de ejecución.
Edición: de acuerdo con el comentario de x4u, el código de bytes de la llamada mantiene una referencia a la firma completa cuando se compila, por lo que la JVM llama al método correcto. Dado que el compilador probablemente fue capaz de decir qué método estaba llamando debido a su acceso a la fuente (y por lo tanto a la información genérica), el compilador probablemente lo vinculó al método correcto a través de toda la firma. Interesante saber!