为什么 Collection 接口里的 contains 和 remove 方法的形参类型是 Object,而不是类型参数 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
amiwrong123
V2EX    Java

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

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

    在 Collection 接口里:

    boolean add(E e); boolen 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     Solana     2474 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 15:25 PVG 23:25 LAX 07:25 JFK 10:25
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86