为什么 Collection 接口里的 contains 和 remove 方法的形参类型是 Object,而不是类型参数

2020-01-01 14:41:01 +08:00
 amiwrong123

在 Collection 接口里:

boolean add(E e);
boolean remove(Object o);//明明 add 都是类型参数呢
boolean contains(Object o);

还有 Map 接口也是:

V setValue(V value);
boolean equals(Object o);
3146 次点击
所在节点    Java
14 条回复
brucefu
2020-01-01 14:55:20 +08:00
引入泛型的目的是:在编译期间起到类型检查机制。remove、contains、equals 方法在使用时没必要进行类型检查
amiwrong123
2020-01-01 15:01:18 +08:00
比如 HashMap 里的 EntrySet 内部类,继承了 AbstractSet,要重写实现 contains 和 remove 方法,还得强转。
<img src="https://s2.ax1x.com/2020/01/01/lGYRmV.png" alt="lGYRmV.png" border="0">
Cbdy
2020-01-01 15:12:04 +08:00
Collection 应该是在 1.5 之前就在 JDK 的,那个时候还没有泛型
Cbdy
2020-01-01 15:15:52 +08:00
@Cbdy 我看错了。应该是没必要
liuming
2020-01-01 16:36:07 +08:00
xingda920813
2020-01-01 22:09:21 +08:00
remove 的逻辑是看这个 Collection 的 item 和你传入的要删除的对象是不是 equals,而 Object.equals(Object o) 的逻辑完全是用户自定义的,比如一个 Dog 也可能和一个 Cat 是 equals 的。那从一个 Collection<Dog> 里尝试 remove 一个 Cat 就说的通了。
amiwrong123
2020-01-02 09:45:55 +08:00
@xingda920813
但是,既然 remove 里会用到 equals,那么 Dog 类的重写的 equals 肯定会去判断一下`o instanceof Dog`啊,这个通不过就直接返回 false 了啊。也就不能在 Dog 的集合删除 Cat 了啊。
amiwrong123
2020-01-02 09:51:34 +08:00
@brucefu
可是直觉上,总感觉 contains 和 remove 方法也应该检查啊。😂

我看网上还有一种观点,如果类型参数是通配符`? extends Dog`,像 add 这样的函数就无法使用了,但由于 contains 和 remove 方法的形参类型是 Object,所以这两个函数还能继续使用。
amiwrong123
2020-01-02 09:53:37 +08:00
@Cbdy
希望有一个强有力的例子来说服自己😂怎么个没必要法
amiwrong123
2020-01-02 09:58:27 +08:00
@liuming
看了最高答案,这句话 The example given by him is an intersection of a List of Numbers and a List of Longs.比较在意这个例子到底是啥,难道是在那个视频链接,算了,下班了再看。。。
xingda920813
2020-01-02 10:20:58 +08:00
@amiwrong123 通常来说 Dog 重写的 equals 会判断是不是 instanceOf Dog,但那是工程上的实践。从语言和虚拟机的层面没有任何限制一个 Dog 不能跟一个 Cat 相等。

比如说 Dog.equals(Object o):
if (o instanceOf Cat) return true;
xingda920813
2020-01-02 10:24:27 +08:00
@amiwrong123 或者换一个工程中更实际的例子,比如每个 Dog 都有个全局唯一的 id 属性。equals() 只判断 id 是不是相等,id 相同就认为是同一个 Dog,不看其他属性。

那对于一个 Collection<Dog> 我就可以调用 remove((int) 1024) 来删除编号为 1024 的 Dog 了,不需要构造一个 Dog 对象传入 remove。
liuming
2020-01-02 14:10:20 +08:00
@amiwrong123
大意是有两个 List,list1 装 Long,list2 装各种 Number,那个 intersection 操作是 list1.retainAll(list2),去掉 list1 中不存在于 list2 的元素,Josh Bloch 说这是 reasonable 的操作,但是如果添加了泛型限制,就没法兼容了,所以他们没有把所有方法都改成泛型的
cruii
2020-01-03 17:19:31 +08:00
JDK 开发者可不管你到底如何用 remove,如何写 equals 和 hashcode。只需要保证在使用时能正确的实现所需要的功能就行。

一个存储了 14 亿个 Person 对象的 List,我要在 2020 年消灭贫困人口,那我定义的贫困人口就是月收入等于 1000 的人。
定义两个类,一个类是 Person,一个类是 Poor,两个类都只有 2 个字段,String name 和 int salary。
Person 类的 equals 和 hashCode 内容为

@Override
public boolean equals(Object obj) {
Poor p = (Poor) obj;
return this.getMoney() == p.getMoney();
}

@Override
public int hashCode() {
return Objects.hash(this.getMoney());
}
Poor 类的 equals 和 hashCode 内容为
@Override
public boolean equals(Object obj) {
Person p = (Person) obj;
return this.getMoney() == p.getMoney();
}

@Override
public int hashCode() {
return Objects.hash(this.getMoney());
}

准备消灭贫困人口
先创建一个容纳 14 亿个 Person 对象的 List

List<Person> people = new ArrayList<>(.....);
people.add(..);
.....
.....

再创建一个贫困人口对象来代表我定义的贫困人口
Poor poor = new Poor("cruii", 1000); // 就是我为例子了
现在开始消灭

while (people.contains(poor)) {
people.remove(poor);
}

好了,全部跟我一样贫困的全被消灭了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/634167

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX