为什么java.util.Properties实现Map <Object,Object>而不是Map <String,String>
java.util.Properties
类是为了表示键和值都是string的映射。 这是因为Properties
对象用于读取文本文件.properties
文件。
那么,为什么在Java 5中他们改造了这个类来实现Map<Object,Object>
而不是Map<String,String>
呢?
javadoc说:
由于Properties从Hashtableinheritance,put和putAll方法可以应用于Properties对象。 强烈build议不要使用它们,因为它们允许调用者插入其键或值不是string的条目。 应该使用setProperty方法。 如果store或save方法在包含非String键或值的“受损”Properties对象上调用,则调用将失败。
由于键和值都被认为是string,那么为什么不使用适当的generics来强制执行?
我猜使Properties
实现Map<String,String>
不能完全向后兼容为Java 5以前版本编写的代码。如果你有老的代码将非string粘贴到一个Properties对象中,那么这个代码将不再使用Java 5进行编译但是…不是件好事吗? generics的全部不是在编译时捕获这种types的错误吗?
因为他们在Java早期做得很快,并没有意识到后来的版本会有什么变化。
generics被认为是从一开始就是Javadevise的一部分,但是这个特性被放弃了,因为它太复杂了,在当时是不必要的。 因此,标准库中的很多代码都是在假设非generics集合的情况下编写的。 马丁·奥德斯基(Martin Odersky)用原型语言“比萨”(Pizza)来展示它们如何在保持接近完美的向后兼容性的同时,以Java代码和字节码的方式完成。 这个原型导致了Java 5,其中集合类被改进为generics,允许旧代码继续工作。
不幸的是,如果他们追溯地从Map<String, String>
inheritance了Properties
,那么以下先前有效的代码将停止工作:
Map<Object, Object> x = new Properties() x.put("flag", true)
为什么有人会这么做呢,但是Sun对Java向后兼容的承诺已经超越了英雄般的意义。
现在大部分受过教育的观察者所赞赏的是, Properties
决不应该从Map
inheritance。 它应该围绕着Map
,只展示那些有意义的Map的特性。
马丁·奥德斯基(Martin Odersky)自从重塑Java以来,一直致力于开发更新颖的Scala语言,inheritance更less的错误,在一些领域开创了新的天地。 如果你发现Java的烦人的烦恼,请看看它。
最初的意图是, Properties
确实会扩展Hashtable<String,String>
。 不幸的是桥接方法的实现引起了一个问题。 以这种方式定义的Properties
会导致javac生成合成方法。 Properties
应该定义一个返回String
的get
方法,但是需要重写一个返回Object
的方法。 所以join了一种合成桥梁的方法。
假设你有一个在老的1.4天里写的课。 你已经覆盖了Properties
一些方法。 但是你没有做的是重写新的方法。 这导致意想不到的行为。 为了避免这些桥接方法, Properties
扩展了Hashtable<Object,Object>
。 同样, Iterable
不会返回(只读) SimpleIterable
,因为这会将方法添加到Collection
实现中。
用于创build来自属性的Map的单线(不带警告的双线):
@SuppressWarnings({ "unchecked", "rawtypes" }) Map<String, String> sysProps = new HashMap(System.getProperties());
向后兼容。
原因: Liskov替代原则和向后兼容。 Properties
扩展了Hashtable
,因此必须接受Hashtable
可以接受的所有消息 – 这意味着接受put(Object, Object)
。 而且它必须扩展纯Hashtable
而不是Hashtable<String, String>
因为generics是通过types擦除以向下兼容的方式实现的,所以一旦编译器做了它的事情,就没有generics。