java ¿Cuáles son las razones por las que Map.get (Clave de objeto) no es (totalmente) genérico?



5 Answers

Kevin Bourrillion, un asombroso programador de Java en Google, escribió sobre este problema exactamente en una publicación de blog hace un tiempo (sin duda en el contexto de Set lugar de Map ). La frase más relevante:

De manera uniforme, los métodos del Java Collections Framework (y también la Biblioteca de Google Collections) nunca restringen los tipos de sus parámetros, excepto cuando es necesario para evitar que la colección se rompa.

No estoy del todo seguro de estar de acuerdo con esto como principio, por ejemplo .NET parece estar bien y requiere el tipo de clave correcto, pero vale la pena seguir el razonamiento en la publicación del blog. (Habiendo mencionado .NET, vale la pena explicar que parte de la razón por la que no es un problema en .NET es que existe el problema más grande en .NET de una varianza más limitada ...)

java generics collections map

¿Cuáles son las razones detrás de la decisión de no tener un método de obtención totalmente genérico en la interfaz de java.util.Map<K, V> .

Para aclarar la cuestión, la firma del método es

V get(Object key)

en lugar de

V get(K key)

y me pregunto por qué (lo mismo para remove, containsKey, containsValue ).




Es una aplicación de la Ley de Postel, "sé conservador en lo que haces, sé liberal en lo que aceptas de los demás".

Se pueden realizar verificaciones de igualdad sin importar el tipo; El método equals se define en la clase Object y acepta cualquier Object como parámetro. Por lo tanto, tiene sentido que la equivalencia de claves y las operaciones basadas en la equivalencia de claves acepten cualquier tipo de Object .

Cuando un mapa devuelve valores clave, conserva tanta información de tipo como puede, mediante el uso del parámetro type.




La razón es que la contención está determinada por equals y hashCode que son métodos en Object y ambos toman un parámetro Object . Este fue un defecto de diseño temprano en las bibliotecas estándar de Java. Junto con las limitaciones en el sistema de tipos de Java, obliga a todo lo que se basa en iguales y hashCode a tomar Object .

La única forma de tener tablas hash seguras para los tipos y la igualdad en Java es evitar Object.equals y Object.hashCode y usar un sustituto genérico. Java funcional viene con clases de tipo para este propósito: Hash<A> e Equal<A> . Se proporciona una envoltura para HashMap<K, V> que toma Hash<K> e Equal<K> en su constructor. Los métodos get y contains esta clase, por lo tanto, toman un argumento genérico de tipo K

Ejemplo:

HashMap<String, Integer> h =
  new HashMap<String, Integer>(Equal.stringEqual, Hash.stringHash);

h.add("one", 1);

h.get("one"); // All good

h.get(Integer.valueOf(1)); // Compiler error



Compatibilidad hacia atrás, supongo. Map (o HashMap ) aún necesita soportar get(Object) .




Compatibilidad.

Antes de que estuvieran disponibles los genéricos, solo se obtenía (Objeto o).

Si hubieran cambiado este método para obtener (<K> o), potencialmente habría forzado el mantenimiento masivo de código en los usuarios de Java solo para que el código de trabajo se compile nuevamente.

Podrían haber introducido un método adicional , digamos get_checked (<K> o) y dejar de usar el antiguo método get () para que hubiera una ruta de transición más suave. Pero por alguna razón, esto no se hizo. (La situación en la que estamos ahora es que necesita instalar herramientas como findBugs para verificar la compatibilidad de tipos entre el argumento get () y el tipo de clave declarado <K> del mapa).

Los argumentos relacionados con la semántica de .equals () son falsos, creo. (Técnicamente son correctos, pero sigo creyendo que son falsos. Ningún diseñador en su sano juicio logrará que o1.equals (o2) sea verdadero si o1 y o2 no tienen una superclase común).




Related