WMR*_*WMR 395 java generics collections map
什么是决定不具有的接口完全通用的get方法背后的原因java.util.Map<K, V>
.
为了澄清这个问题,方法的签名是
V get(Object key)
代替
V get(K key)
我想知道为什么(同样的事情remove, containsKey, containsValue
).
new*_*cct 258
正如其他人所提到的,之所以get()
等等不是通用的,因为您要检索的条目的键不必与传入的对象的类型相同get()
; 方法的规范只要求它们相等.这取决于该equals()
方法如何将Object作为参数,而不仅仅是与对象相同的类型.
尽管通常equals()
可以确定许多类已定义,以使其对象只能等于其自己的类的对象,但Java中有许多地方并非如此.例如,List.equals()
如果两个List对象都是Lists并且具有相同的内容,则表示两个List对象相等的规范,即使它们是不同的实现List
.所以回到这个问题的例子,根据方法的规范可以有一个Map<ArrayList, Something>
和我get()
用LinkedList
as参数调用,它应该检索具有相同内容的列表的键.如果get()
是通用的并且限制其参数类型,则这是不可能的.
Jon*_*eet 105
一个真棒的Java编码器在谷歌,凯文Bourrillion,写了一篇关于正是这个问题在博客文章(当然在的背景下,前一段时间Set
,而不是Map
).最相关的句子:
统一地,Java Collections Framework(以及Google Collections Library)的方法从不限制其参数的类型,除非必须防止集合被破坏.
我并不完全确定我同意它作为一个原则 - 例如,.NET似乎很好,需要正确的密钥类型 - 但值得遵循博客文章中的推理.(提到.NET之后,值得解释的是,在.NET中不存在问题的一部分原因是.NET中存在更大限制的方差问题......)
Bri*_*new 29
合同表达如下:
更正式地说,如果此映射包含从键k到值v的映射,使得(key == null?k == null: key.equals(k)),则此方法返回v; 否则返回null.(最多可以有一个这样的映射.)
(我的重点)
因此,成功的键查找取决于输入键的相等方法的实现.这不一定取决于k的类别.
eri*_*son 17
这是Postel定律的一个应用, "在你所做的事情上要保守,在你接受别人的事情上保持自由."
无论何种类型,都可以进行平等检查; 该equals
方法在Object
类上定义,并接受any Object
作为参数.因此,关键等价和基于键等价的操作接受任何Object
类型都是有意义的.
当映射返回键值时,通过使用type参数,它可以保留尽可能多的类型信息.
Yar*_*ena 12
我认为Generics Tutorial的这一部分解释了这种情况(我的重点):
"您需要确保通用API不会过度限制;它必须继续支持API的原始合同.再次考虑java.util.Collection中的一些示例.预通用API如下所示:
interface Collection {
public boolean containsAll(Collection c);
...
}
Run Code Online (Sandbox Code Playgroud)
一种天真的尝试,它是:
interface Collection<E> {
public boolean containsAll(Collection<E> c);
...
}
Run Code Online (Sandbox Code Playgroud)
虽然这当然是类型安全的,但它不符合API的原始合同. containsAll()方法适用于任何类型的传入集合.只有当传入的集合实际上只包含E的实例时,它才会成功,但是:
原因是遏制是由方法决定的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
Run Code Online (Sandbox Code Playgroud)
小智 5
还有一个更重要的原因,技术上做不到,因为它破坏了Map。
Java 有像<? extends SomeClass>
. 标记的此类引用可以指向带有签名的类型<AnySubclassOfSomeClass>
。但是多态泛型使该引用成为readonly。编译器只允许您使用泛型类型作为方法的返回类型(如简单的 getter),但会阻止使用泛型类型为参数的方法(如普通的 setter)。这意味着如果你写Map<? extends KeyType, ValueType>
,编译器不允许你调用 method get(<? extends KeyType>)
,那么映射就没有用了。唯一的解决方案是使此方法不通用:get(Object)
.
兼容性。
在泛型可用之前,只有 get(Object o)。
如果他们将此方法更改为 get(<K> o) 它可能会迫使 Java 用户进行大量代码维护,只是为了再次编译工作代码。
他们本可以引入一个额外的方法,比如 get_checked(<K> o) 并弃用旧的 get() 方法,所以有一个更温和的过渡路径。但由于某种原因,这并没有完成。(我们现在的情况是你需要安装 findBugs 之类的工具来检查 get() 参数和映射的声明键类型 <K> 之间的类型兼容性。)
我认为与 .equals() 语义相关的论点是虚假的。(从技术上讲,它们是正确的,但我仍然认为它们是假的。如果 o1 和 o2 没有任何共同的超类,那么头脑正常的设计师不会让 o1.equals(o2) 为真。)
归档时间: |
|
查看次数: |
51801 次 |
最近记录: |