为什么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以获得额外的类型安全性。