为什么java.util.Properties实现Map <object,object>而不是Map <string,string> </string,string> </object,object>

时间:2009-05-16 23:17:02

标签: java generics collections

java.util.Properties类用于表示键和值都是字符串的映射。这是因为Properties个对象用于读取.properties文件,这些文件是文本文件。

那么,为什么在Java 5中他们改进了这个类以实现Map<Object,Object>而不是Map<String,String>

javadoc州:

  

因为Properties继承自Hashtable,所以put和putAll方法可以应用于Properties对象。强烈建议不要使用它们,因为它们允许调用者插入其键或值不是字符串的条目。应该使用setProperty方法。如果在包含非String键或值的“受损”Properties对象上调用store或save方法,则调用将失败。

由于键和值都应该是字符串,那么为什么不使用正确的泛型类型静态强制执行呢?

我认为使Properties实现Map<String,String>不能与为Java 5之前编写的代码完全向后兼容。如果你有旧的代码将非字符串粘贴到Properties对象中,那么该代码将不再用Java 5编译。但是......这不是一件好事吗?在编译时捕获这种类型错误不是泛型的全部意义吗?

5 个答案:

答案 0 :(得分:53)

因为他们在Java早期就匆匆忙忙做了这件事,并且没有意识到后来四个版本会带来什么影响。

泛型应该从一开始就是Java设计的一部分,但是由于过于复杂而且当时没有必要,因此该功能被删除了。因此,标准库中的大量代码是在非泛型集合的假设下编写的。它采用了Martin Odersky的原型语言“Pizza”,展示了如何在保持近乎完美的向后兼容性的同时,使用Java代码和字节码完成它们。原型开发了Java 5,其中的集合类使用泛型进行了改进,允许旧代码继续工作。

不幸的是,如果他们追溯Properties继承Map<String, String>,则以下有效代码将停止工作:

Map<Object, Object> x = new Properties()
x.put("flag", true)

为什么有人会这样做是超出我的,但是Sun对Java的向后兼容性的承诺已经超越了英雄,变成了毫无意义。

大多数受过教育的观察者现在都认为,Properties永远不应该从Map继承。它应该环绕Map,只暴露那些有意义的Map特征。

自从重新发明Java以来​​,Martin Odersky已经开始创建新的Scala语言,它更清晰,继承更少的错误,并在许多领域开辟了新天地。如果你发现Java的烦恼很烦人,那就去看看吧。

答案 1 :(得分:27)

最初的意图是Properties确实会扩展Hashtable<String,String>。不幸的是,桥接方法的实现引起了一个问题。以这种方式定义的Properties会导致javac生成合成方法。 Properties应该定义一个get方法,该方法返回String但需要覆盖返回Object的方法。因此增加了合成桥接方法。

假设你有一个写在糟糕的旧1.4天的课程。你已经覆盖了Properties中的一些方法。但是你没有做的是覆盖了新的方法。这会导致意外行为。为了避免这些桥接方法,Properties扩展了Hashtable<Object,Object>。同样地,Iterable不返回(只读)SimpleIterable,因为这会为Collection实现添加方法。

答案 2 :(得分:13)

用于从属性创建Map的单行(双线无警告):

@SuppressWarnings({ "unchecked", "rawtypes" })
Map<String, String> sysProps = new HashMap(System.getProperties());

答案 3 :(得分:4)

向后兼容性。

答案 4 :(得分:2)

原因:Liskov substitution principle和向后兼容性。 Properties扩展Hashtable,因此必须接受Hashtable可接受的所有邮件 - 这意味着接受put(Object, Object)。并且它必须扩展普通Hashtable而不是Hashtable<String, String>,因为泛型是通过type erasure以向下兼容的方式实现的,所以一旦编译器完成了它的事情,就没有泛型。 / p>