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

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

V get(对象键)

而不是

V get(K键)

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


当前回答

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

“你需要确保通用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。”

其他回答

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

在Java中获得类型安全哈希表和相等性的唯一方法是避免使用Object。equals和Object。hashCode并使用通用的替代品。函数式Java提供的类型类就是为了这个目的:Hash<A>和Equal<A>。提供了HashMap<K, V>的包装器,在其构造函数中接受Hash<K>和Equal<K>。因此,该类的get和contains方法采用类型为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

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

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).

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

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

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

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

“你需要确保通用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。”

合同的表述如下:

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

(我的重点)

因此,成功的键查找取决于输入键对等式方法的实现。它不一定依赖于k的类。