决定不使用完全泛型的get方法的原因是什么 在java.util接口中。地图< K、V >。

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

V get(对象键)

而不是

V get(K键)

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


当前回答

谷歌的一名出色的Java编码器Kevin Bourrillion不久前在一篇博客文章中就这个问题写了一篇文章(当然是在Set而不是Map的上下文中)。最相关的句子:

的方法 Collections框架(和谷歌. Collections框架) 收藏图书馆也是)从不 限制其参数的类型 除非是为了预防 收集从破碎。

我不完全确定我是否同意这一原则——例如,. net似乎需要正确的键类型就可以了——但值得遵循博客文章中的推理。(提到。net之后,有必要解释一下,为什么在。net中它不是问题的部分原因是。net中存在更大的问题,即更有限的方差…)

其他回答

正如其他人所提到的,get()等不是泛型的原因是,您正在检索的条目的键不必与传递给get()的对象的类型相同;该方法的规范只要求它们相等。这源于equals()方法如何接受Object作为参数,而不仅仅是对象的相同类型。

Although it may be commonly true that many classes have equals() defined so that its objects can only be equal to objects of its own class, there are many places in Java where this is not the case. For example, the specification for List.equals() says that two List objects are equal if they are both Lists and have the same contents, even if they are different implementations of List. So coming back to the example in this question, according to the specification of the method is possible to have a Map<ArrayList, Something> and for me to call get() with a LinkedList as argument, and it should retrieve the key which is a list with the same contents. This would not be possible if get() were generic and restricted its argument type.

向后兼容,我想。Map(或HashMap)仍然需要支持get(Object)。

还有一个更重要的原因,它在技术上不能做到,因为它破坏了地图。

Java has polymorphic generic construction like <? extends SomeClass>. Marked such reference can point to type signed with <AnySubclassOfSomeClass>. But polymorphic generic makes that reference readonly. The compiler allows you to use generic types only as returning type of method (like simple getters), but blocks using of methods where generic type is argument (like ordinary setters). It means if you write Map<? extends KeyType, ValueType>, the compiler does not allow you to call method get(<? extends KeyType>), and the map will be useless. The only solution is to make this method not generic: get(Object).

我们正在做大的重构,我们错过了这个强类型的get(),以检查我们是否错过了旧类型的get()。

但是我发现了编译时间检查的变通/丑陋的技巧:创建Map接口,强类型get, containsKey, remove…然后放到java中。您的项目的Util包。

你只调用get()就会得到编译错误,…对于错误的类型,其他的编译器似乎都可以(至少在eclipse kepler内部)。

不要忘记在检查构建后删除此接口,因为这不是您在运行时想要的。

我认为泛型教程的这一部分解释了这种情况(我的重点):

“你需要确保通用API没有过度限制;它必须 继续支持原API合同。再来看一些例子 从java.util.Collection。泛型前API看起来像这样:

interface Collection { 
  public boolean containsAll(Collection c);
  ...
}

一种天真的概括是:

interface Collection<E> { 
  public boolean containsAll(Collection<E> c);
  ...
}

虽然这肯定是类型安全的,但它不符合API的原始契约。 containsAll()方法适用于任何类型的传入集合。它只会 如果传入的集合真的只包含E的实例,则成功,但是:

传入的静态类型 收藏可能有所不同 因为调用者不知道 集合的精确类型 传入,或者因为它是 集合<S>,其中S是a E的子类型。 它是完美的 使用containsAll()调用是合法的 不同类型的集合。的 程序应该工作,返回false。”