V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
amiwrong123
V2EX  ›  Java

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

  •  
  •   amiwrong123 · 2020-01-01 14:41:01 +08:00 · 3121 次点击
    这是一个创建于 1792 天前的主题,其中的信息可能已经有所发展或是发生改变。

    在 Collection 接口里:

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

    还有 Map 接口也是:

    V setValue(V value);
    boolean equals(Object o);
    
    第 1 条附言  ·  2020-01-01 15:15:22 +08:00

    比如 HashMap 里的 EntrySet 内部类,继承了 AbstractSet,要重写实现 contains 和 remove 方法,还得强转形参。 lGYRmV.png

    14 条回复    2020-01-03 17:19:31 +08:00
    brucefu
        1
    brucefu  
       2020-01-01 14:55:20 +08:00
    引入泛型的目的是:在编译期间起到类型检查机制。remove、contains、equals 方法在使用时没必要进行类型检查
    amiwrong123
        2
    amiwrong123  
    OP
       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
        3
    Cbdy  
       2020-01-01 15:12:04 +08:00 via Android
    Collection 应该是在 1.5 之前就在 JDK 的,那个时候还没有泛型
    Cbdy
        4
    Cbdy  
       2020-01-01 15:15:52 +08:00
    @Cbdy 我看错了。应该是没必要
    liuming
        5
    liuming  
       2020-01-01 16:36:07 +08:00
    xingda920813
        6
    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
        7
    amiwrong123  
    OP
       2020-01-02 09:45:55 +08:00
    @xingda920813
    但是,既然 remove 里会用到 equals,那么 Dog 类的重写的 equals 肯定会去判断一下`o instanceof Dog`啊,这个通不过就直接返回 false 了啊。也就不能在 Dog 的集合删除 Cat 了啊。
    amiwrong123
        8
    amiwrong123  
    OP
       2020-01-02 09:51:34 +08:00
    @brucefu
    可是直觉上,总感觉 contains 和 remove 方法也应该检查啊。😂

    我看网上还有一种观点,如果类型参数是通配符`? extends Dog`,像 add 这样的函数就无法使用了,但由于 contains 和 remove 方法的形参类型是 Object,所以这两个函数还能继续使用。
    amiwrong123
        9
    amiwrong123  
    OP
       2020-01-02 09:53:37 +08:00
    @Cbdy
    希望有一个强有力的例子来说服自己😂怎么个没必要法
    amiwrong123
        10
    amiwrong123  
    OP
       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
        11
    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
        12
    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
        13
    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
        14
    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);
    }

    好了,全部跟我一样贫困的全被消灭了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3235 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 36ms · UTC 00:17 · PVG 08:17 · LAX 16:17 · JFK 19:17
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.