Java Map.get(对象键)不是(完全)通用的原因是什么

Java Map.get(对象键)不是(完全)通用的原因是什么,java,generics,collections,map,Java,Generics,Collections,Map,决定不使用完全通用的get方法的原因是什么 在的界面中 为了澄清这个问题,方法的签名是 V get(对象键) 而不是 V get(K键) 我想知道为什么(对于remove、containsKey、containsValue都是一样的)。我猜是向后兼容Map(或HashMap)仍需支持get(Object)合同表述如下: 更正式地说,如果此地图包含 从键k到值v的映射,如 (key==null?k==null: 键。等于(k)),则此方法 返回v;否则返回null。 (最多可以有一个 映射。) (

决定不使用完全通用的get方法的原因是什么 在的界面中

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

V get(对象键)

而不是

V get(K键)


我想知道为什么(对于
remove、containsKey、containsValue都是一样的)。

我猜是向后兼容
Map
(或
HashMap
)仍需支持
get(Object)

合同表述如下:

更正式地说,如果此地图包含 从键k到值v的映射,如 (key==null?k==null: 键。等于(k)),则此方法 返回v;否则返回null。 (最多可以有一个 映射。)

(我的重点)


因此,成功的键查找取决于输入键对相等方法的实现。这并不一定取决于k的阶级。

谷歌一位出色的Java程序员凯文·布瑞利昂(Kevin Bourrillion)不久前就写过这个问题(无可否认是在
Set
而不是
Map
的背景下写的)。最相关的一句话:

Java的方法是一致的 集合框架(和Google 收藏(图书馆也一样)永远不会 限制其参数的类型 除非有必要预防 这些收藏品不会被损坏


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

原因是包含由
equals
hashCode
确定,这两种方法都是对
对象
的方法,并且都采用
对象
参数。这是Java标准库中的早期设计缺陷。再加上Java类型系统的局限性,它强制依赖于equals和hashCode的任何东西都接受
对象

在Java中拥有类型安全哈希表和相等的唯一方法是避免使用
Object.equals
Object.hashCode
,并使用泛型替换。附带的类型类仅用于此目的:和。提供的包装器在其构造函数中采用
散列
相等
。此类的
get
包含
方法,因此采用
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
HashMap h=
新的HashMap(Equal.stringEqual,Hash.stringHash);
h、 添加(“一”,1);
h、 获取(“一”);//一切都好
h、 get(Integer.valueOf(1));//编译错误

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

虽然许多类都定义了
equals()
,使其对象只能与自己类的对象相等,这一点通常是正确的,但Java中有许多地方并非如此。例如,
List.equals()
的规范指出,如果两个列表对象都是列表并且具有相同的内容,则它们是相等的,即使它们是
List
的不同实现。所以回到这个问题中的例子,根据方法的规范,可以有一个
映射
,我可以用
LinkedList
作为参数调用
get()
,它应该检索一个键,它是一个具有相同内容的列表。如果
get()
是泛型的并且限制了它的参数类型,这将是不可能的。

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

“您需要确保通用API没有过度限制;它必须 继续支持API的原始合同。 来自java.util.Collection。预泛型API如下所示:

interface Collection { 
  public boolean containsAll(Collection c);
  ...
}
将其泛化的天真尝试是:

interface Collection<E> { 
  public boolean containsAll(Collection<E> c);
  ...
}
接口集合{
公共图书馆(c组);
...
}
虽然这肯定是类型安全的,但它不符合API的原始合同。 containsAll()方法适用于任何类型的传入集合 如果传入集合确实只包含E的实例,则成功,但:

  • 传入文件的静态类型 收集可能会有所不同 因为打电话的人不知道电话号码 正在创建的集合的精确类型 通过了,或者可能是因为它是一个 集合,其中S是 E
  • 非常好 调用containsAll()的合法性 一个不同类型的集合 例行程序应该有效,返回false。“
这是一种“做事要保守,接受别人的东西要自由”的应用

无论类型如何,都可以执行相等性检查;
equals
方法在
对象
类上定义,并接受任何
对象
作为参数。因此,接受任何
对象
类型对于键等价和基于键等价的操作是有意义的


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

还有一个更重要的原因,它不能在技术上完成,因为它会破坏映射


Java有多态的泛型结构,比如
,我在看这篇文章,并思考他们为什么这样做。我认为现有的任何答案都不能解释为什么他们不能直接