为什么Java的TreeSet remove(Object)不带E
从Java 6 TreeSet
文档:
boolean remove(Object o): Removes the specified element from this set if it is present.
为什么这会接受Object而不是genericsE? 唯一可以添加的对象是E类型,因此唯一的可移动类型应该是E类型。
remove()
,就像get()
在给定一个相等的元素(就.equals()
)时需要工作一样。 在Java中,不同类的对象可能(在某些情况下是必需的)是相同的。 因此,您不应该限制类型。
从第一条评论中回答:
神话:
一个流行的神话是它是愚蠢和邪恶的,但由于向后兼容性,它是必要的。 但兼容性论点无关紧要; 无论您是否考虑兼容性,API都是正确的。
真正的原因:
统一地,Java Collections Framework(以及Google Collections Library)的方法从不限制其参数的类型,除非必须防止集合被破坏。
在这里阅读更多内容: 为什么Set.contains()采用对象而不是E?
好吧,每个E也是一个对象,也许你现在的E不是E(例如来自事件源),这对你来说很方便。 否则你只需要将其强制转换为E即可将其删除。
从平等的角度来看,这并不重要:如果给定对象的引用地址等于集合的内容,则对其进行测试,因此它与哪个类无关。
这确实是个问题。 如果有人调用remove(o)
而o
的类型不是E
,那么通常是编程错误试图删除错误的东西。 类型检查无法保护我们免受错误的影响。
虽然一个好的IDE(IntelliJ)可以检测到这些问题并警告我们,但API设计者应该提供更精确的签名来利用编译器类型检查。 (IDE在这里作弊 – 它知道Set.remove()
的含义,因为它是一个标准Set.remove()
不会为自定义API提供相同的帮助)
对于像contains()
这样的查询API,可以接受非E
参数并返回一个简单的false。 所以我们可以同时拥有
boolean contains(Object o); boolean contains2(E o);
对于像remove()
这样的变异API,它是否应该接受非E
参数是有争议的。 然而,鉴于擦除的现实,辩论将没有实际意义 – 除了接受非E论证并对此保持沉默之外别无选择。 我们仍然可以有两种方法
boolean remove(Object o); boolean remove2(E o);
在大多数情况下,程序员可以调用contains2/remove2
以获得额外的类型安全性。