new - java value pair list
¿Por qué java.util.Properties implementa Map<Object, Object> y no Map<String, String> (5)
Compatibilidad al revés.
La clase java.util.Properties
pretende representar un mapa donde las claves y los valores son ambas cadenas. Esto se debe a que los objetos de Properties
se utilizan para leer archivos .properties
, que son archivos de texto.
Entonces, ¿por qué en Java 5 modificaron esta clase para implementar Map<Object,Object>
y no Map<String,String>
?
Los estados de javadoc :
Debido a que Properties hereda de Hashtable, los métodos put y putAll se pueden aplicar a un objeto Properties. Se desaconseja enérgicamente su uso ya que permiten a la persona que llama insertar entradas cuyas claves o valores no son cadenas. El método setProperty se debe usar en su lugar. Si se llama al método store o save en un objeto Properties "comprometido" que contiene una clave o un valor que no sea String, la llamada fallará.
Dado que se supone que las claves y los valores son cadenas, ¿por qué no aplicarlo estáticamente utilizando el tipo genérico adecuado?
Supongo que hacer que Properties
implemente Map<String,String>
no sería totalmente retrocompatible con el código escrito para pre-Java 5. Si tiene un código anterior que adhiere cadenas a un objeto Properties, entonces ese código ya no compilaría con Java 5 Pero ... ¿no es eso algo bueno? ¿No es el objetivo de los genéricos detectar estos tipos de errores en tiempo de compilación?
El motivo: el principio de sustitución de Liskov y la compatibilidad con versiones anteriores. Properties
amplían Hashtable
y, por lo tanto, deben aceptar todos los mensajes que Hashtable
aceptaría, y eso significa aceptar put(Object, Object)
. Y tiene que extender Hashtable
simple en lugar de Hashtable<String, String>
porque los genéricos se implementaron en la forma de compatibilización hacia abajo mediante el borrado de tipo , por lo que una vez que el compilador ha hecho lo suyo, no hay genéricos.
Originalmente se pretendía que Properties
extendiera Hashtable<String,String>
. Lamentablemente, la implementación de métodos de puente causó un problema. Properties
definidas de tal manera causan que javac genere métodos sintéticos. Properties
deben definir, por ejemplo, un método get
que devuelva una String
pero debe sobrescribir un método que devuelva Object
. Entonces se agrega un método de puente sintético.
Supongamos que tiene una clase escrita en los malos viejos 1.4 días. Ha anulado algunos métodos en Properties
. Pero lo que no ha hecho se anula los nuevos métodos. Esto conduce a un comportamiento involuntario. Para evitar estos métodos de puente, Properties
extends Hashtable<Object,Object>
. De forma similar, Iterable
no devuelve un SimpleIterable
(de solo lectura), porque eso habría agregado métodos a las implementaciones de Collection
.
Porque lo hicieron con prisa en los primeros días de Java, y no se dieron cuenta de cuáles serían las implicaciones en cuatro versiones más adelante.
Se suponía que los genéricos formaban parte del diseño de Java desde el principio, pero la función se descartó por ser demasiado complicada y, en ese momento, innecesaria. Como resultado, muchos códigos en las bibliotecas estándar se escriben con la suposición de colecciones no genéricas. Tomó el lenguaje prototipo "Pizza" de Martin Odersky para mostrar cómo se podían hacer bastante bien manteniendo una compatibilidad casi perfecta con códigos Java y bytecode. El prototipo condujo a Java 5, en el cual las clases de colecciones fueron modernizadas con genéricos de una manera que permitía que el código viejo siguiera funcionando.
Desafortunadamente, si tuvieran que retroactivamente hacer que Properties
heredara de Map<String, String>
, entonces el siguiente código previamente válido dejaría de funcionar:
Map<Object, Object> x = new Properties()
x.put("flag", true)
Por qué alguien haría eso está más allá de mí, pero el compromiso de Sun con la compatibilidad con versiones anteriores en Java ha pasado de ser heroico a ser inútil.
Lo que ahora aprecian la mayoría de los observadores educados es que las Properties
nunca deberían haber heredado de Map
en absoluto. En su lugar, debe envolver Map
, exponiendo solo aquellas características de Map que tengan sentido.
Desde la reinvención de Java, Martin Odersky ha creado el nuevo lenguaje Scala, que es más limpio, hereda menos errores y abre nuevos caminos en varias áreas. Si encuentra las molestias de Java molestas, échele un vistazo.
Un trazador de líneas (dos líneas para no advertencias) para crear un mapa a partir de las propiedades:
@SuppressWarnings({ "unchecked", "rawtypes" })
Map<String, String> sysProps = new HashMap(System.getProperties());