【问题标题】:Why aren't Collections.checkedMap and friends used more commonly?为什么 Collections.checkedMap 和朋友不更常用?
【发布时间】:2011-01-11 21:03:30
【问题描述】:

我最近偶然发现了用于创建标准集合类型的动态类型安全视图的Collection.checkedMap 系列函数的 Javadoc。考虑到它们在诊断相对常见的程序员错误的集合之上添加了另一层安全性,我会认为它们会更受欢迎。不过,出于某种原因,在我参与过的所有大型 Java 项目中,我从未见过它们使用过一次。

我的问题是:Java 程序员不更频繁地使用这些检查包装器是否有特殊原因?还是只是缺乏利益/不知道他们的存在?

编辑:为了澄清我的问题,集合的通用版本仍然包含类型不安全的函数。例如,MapcontainsKeycontainsValueremoveget 都在 Object 上运行。我的主要问题是,鉴于这种类型不安全,为什么更多人不使用检查过的实现来诊断运行时类型违规。

【问题讨论】:

    标签: java collections


    【解决方案1】:

    只有在滥用(或不使用)泛型时才真正需要这些类。它在运行时重复检查,这些检查应该足以通过泛型引擎的类型安全保证在编译时执行。

    因此,只有在不信任库用户并希望保护自己或用户免受不当使用地图的用户的影响的 API 设计人员的情况下,使用这些方法才真正有趣。

    简而言之:如果您的代码在编译时没有原始类型警告或任何未经检查的转换/类型警告,则保证checked* 方法不会给您带来任何好处;它们只是运行时命中。

    编辑

    您似乎得出的结论是,由于removeget 等在Object 上运行,它们在某种程度上是类型不安全的。他们不是。这些方法都不会将其操作数存储在映射中,因此您当然不会损害映射本身的类型安全性。这些方法采用 Object 的主要原因有一个:向后兼容。从来没有严格要求查找需要遵守同一类。例如,如果 A 类的实例和 B 类的实例可能以某种方式彼此相等,则将 a 放入映射中,然后调用 remove(b) 应该删除 a's映射。

    一旦添加了泛型以保持向后兼容性,就需要支持此行为。因此,他们无法将接口更新为remove(K) 之类的东西,因为现有代码可能依赖于能够在给定完全不同类的实例的情况下删除映射。然而,它不是类型不安全的,并且使用地图的检查版本丝毫不会改变这种行为。

    【讨论】:

    • 拥有这些容器似乎是一个好主意的主要原因是 Map 的 .put() 之类的东西,它采用 Object 而不是 K(在,例如,一个 Map)。不小心将错误类型的对象添加到 Map 中似乎并不是滥用泛型。这更像是一个程序员的错误。
    • @templatetypedef:我不确定你的意思。地图的put 指定为put(K, V)。如果您使用泛型,则不能将任何旧对象放入 Map。
    • @Mark Peters- 我的错误;我的意思是put,它接受Object 而不是Kremove 的方式相同,containsKeycontainsValue 也是如此。
    • @templatetypedef:你又说put了。你的意思是get?无论如何,您提到的这些方法实际上都不会受到使用地图检查版本的影响。仅当在地图中存储某些内容时才进行检查(即put)。在 CheckedMap 上调用 get 并提供非 K 仍然不会引发异常。
    • @Mark Peters- D'oh,我的意思是get。 :-) 另外,感谢您的好回答!这很有道理,我很好奇他们为什么要按照他们的方式设计这些包装器。
    【解决方案2】:

    撤回:以下观点是错误的。除了向后兼容性之外,还有一些很好的理由说明为什么最好将查找索引设为任何类型。

    这些方法必须采用Object 而不是E,这并没有什么神圣的原因。在现实世界的程序中,如果传递了一个非 E 对象,那几乎可以肯定是一个应用程序错误。在大多数情况下,E 版 API 会比 Object 版更好。我说孙在这里犯了一个错误。不存在向后兼容性问题,因为通用 API 是全新的 API。

    幸运的是,如果我将非 E 类型传递给这些方法,我的 IDE (IntelliJ) 会警告我。静态检查已经远远超出了语言规范的规定和编译器的作用。

    【讨论】:

    • 我没有 d/v,但存在向后兼容性问题,并且泛型集合 API 不是“全新的”,它们改造了现有的集合 API。
    • @mark-peters put(K,V) 也破坏了您的“向后兼容性”,因为旧方法没有约束。但是不,不存在这样的兼容性问题,因为任何使用通用接口的代码都是新代码,它们接受新的约束,如果它们可以接受 put(K,V),它们就可以接受 remove(K)。实际上每个人都想要 remove(K) 而不是 remove(Object)。现在回馈我的观点。
    • 这与源代码或字节码兼容性无关。这是关于remove(Object) 和类似方法的合同。当他们用泛型改造集合时,要将remove(Object) 更新为remove(K),他们将不得不更改方法的规范(例如,它在功能上 的作用)。
    • 哦,我同意 remove(K) 将是一个更好的设计选择,如果它今天从头开始设计,如果除了接受 remove(Object) 之外没有其他原因,只是暗示有一个不对称的 equals实施是好的,当它不是。
    猜你喜欢
    • 1970-01-01
    • 2011-04-17
    • 1970-01-01
    • 1970-01-01
    • 2013-02-10
    • 1970-01-01
    • 2020-05-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多