[Java] Map.get(Object key)不是(完全)通用的原因是什么?



Answers

Google的一位出色的Java编码人员Kevin Bourrillion刚才在一篇博客文章中写到了这个问题(当然,在Set而不是Map )。 最相关的句子:

统一地,Java集合框架(以及Google集合库)的方法不会限制其参数的类型,除非有必要防止集合被破坏。

我不完全相信我同意它作为一个原则 - 例如,.NET似乎很好,需要正确的键类型,但值得在博客文章中推理。 (在提到.NET的时候,值得解释的一点是它在.NET中不存在问题的部分原因是.NET中存在更大限度的问题:更有限的方差...)

Question

决定在java.util.Map<K, V>的接口中没有完全通用的get方法的原因是什么?

为了澄清问题,该方法的签名是

V get(Object key)

代替

V get(K key)

我想知道为什么(同样的事情remove, containsKey, containsValue )。




原因是遏制是由equalshashCode决定的,它们是Object上的方法并且都带有Object参数。 这是Java标准库中的一个早期设计缺陷。 再加上Java类型系统中的限制,它强制依赖于equals和hashCode的任何东西都取Object

在Java中具有类型安全散列表和相等性的唯一方法是避开Object.equalsObject.hashCode并使用通用替换。 功能性Java为了这个目的附带了类型类: Hash<A>Equal<A> 。 提供HashMap<K, V>包装器,它在其构造函数中使用Hash<K>Equal<K> 。 这个类的getcontains方法因此采用类型K的泛型参数。

例:

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



我正在看这个,并思考他们为什么这样做。 我不认为任何现有的答案解释了为什么他们不能只让新的通用接口只接受适当的密钥类型。 实际的原因是,即使他们引入了泛型,他们也没有创建一个新的接口。 Map接口与旧的非泛型地图只是泛型和非泛型版本。 这样,如果你有一个接受非通用Map的方法,你可以传递一个Map<String, Customer> ,它仍然可以工作。 同时get的合同接受Object,所以新的接口也应该支持这个合同。

在我看来,他们应该添加一个新的界面并在现有的集合上实现,但他们决定采用兼容的界面,即使这意味着get方法的设计更糟糕。 请注意,集合本身将与现有方法兼容,只有接口不会。




兼容性。

在泛型可用之前,刚刚获得(Object o)。

如果他们改变这个方法来得到(<K> o),它可能会迫使大量的代码维护到Java用户,只是为了再次编译工作代码。

他们可以引入一个额外的方法,比如说get_checked(<K> o),并且弃用旧的get()方法,这样就有了一个更加温和的转换路径。 但由于某种原因,这没有完成。 (我们现在的情况是,你需要安装像findBugs这样的工具来检查get()参数和声明的键值类型<K>之间的类型兼容性。)

有关.equals()的语义的争论是假的,我想。 (从技术上讲,他们是正确的,但我仍然认为他们是假的。如果o1和o2没有任何共同的超类,任何设计师都不会使他的正确思想使o1.equals(o2)成立。)




这是波斯特尔定律的一个应用 “在你做的事情上要保守,在你接受别人的事情时要自由。”

平等检查可以不分类型地进行; equals方法在Object类上定义并接受任何Object作为参数。 因此,对于关键等价和基于关键等价的操作,接受任何Object类型都是有意义的。

当映射返回键值时,它通过使用类型参数保存尽可能多的类型信息。






Links