为什么我们有 contains(Object o) 而不是 contains(E e)?

问题描述 投票:0回答:6

是为了保持与旧(非通用)版本的

Collection
的向后兼容性吗?还是我遗漏了更微妙的细节?我看到这种模式也在
remove
中重复出现 (
remove(Object o)
),但
add
被泛化为
add(E e)

java generics collections contains
6个回答
15
投票

contains()
采用
Object
,因为它匹配的对象不必与您传递给
contains()
的对象具有相同的类型;它只要求它们是平等的。根据
contains()
的规范,如果存在一个对象
contains(o)
使得
e
为 true,则
(o==null ? e==null : o.equals(e))
返回 true。请注意,没有任何要求
o
e
必须是同一类型。这是因为
equals()
方法接受
Object
作为参数,而不仅仅是与对象相同的类型。

虽然许多类都定义了

equals()
,因此其对象只能等于其自己类的对象,但情况并非总是如此。例如,
List.equals()
的规范规定,如果两个
List
对象都是
List
并且具有相同的内容,则它们相等,即使它们是 List 的不同实现。因此,回到这个问题中的示例,可能有一个
Collection<ArrayList>
,并且我可以使用
contains()
作为参数来调用
LinkedList
,并且如果存在具有相同内容的列表,它可能会返回 true。如果
contains()
是通用的并将其参数类型限制为
E
,则这是不可能的。

事实上,

contains()
将任何对象作为参数,这使得您可以使用它来测试集合中是否存在满足特定属性的对象:

Collection<Integer> integers;
boolean oddNumberExists = integers.contains(new Object() {
    public boolean equals(Object e) {
        Integer i = (Integer)e;
        if (i % 2 != 0) return true;
        else return false;
    }
});

5
投票

在此回复。
为什么 Java 集合的删除方法不是通用的?
简而言之,他们希望最大化向后兼容性,因为集合早在泛型之前就已被引入。

我补充一下:他提到的视频值得一看。
http://www.youtube.com/watch?v=wDN_EYUvUq0

更新
澄清一下,(在视频中)说这句话的人是更新 java 地图和集合以使用泛型的人之一。如果他不知道,那是谁呢?


4
投票

这是因为

contains
函数利用了
equals
函数,并且
equals
函数是在 Object 基类中定义的,其签名为
equals(Object o)
而不是
equals(E e)
(因为并非所有类都是通用的)。与
remove
函数的情况相同 - 它使用接受 Object 参数的
equals
函数遍历集合。

但这并不能直接解释这个决定,因为他们仍然可以使用类型 E 并允许它在调用

equals
时自动转换为 Object 类型;但我想他们希望允许在其他对象类型上调用该函数。使用
Collection<Foo> c;
然后调用
c.contains(somethingOfTypeBar)
并没有什么问题 - 它总是返回 false,因此不需要强制转换为 Foo 类型(这可能会引发异常),或者为了防止异常,使用
typeof
打电话。因此,您可以想象,如果您要迭代具有混合类型的内容并在每个元素上调用
contains
,您可以简单地对所有元素使用 contains 函数,而不需要防护。

当你这样看时,它实际上让人想起“较新的”松散类型语言......


0
投票

因为否则它只能与参数类型的精确匹配进行比较,特别是通配符集合将停止工作,例如

class Base
{
}

class Derived
  extends Base
{
}

Collection< ? extends Base > c = ...;

Derived d = ...;

Base base_ref = d;

c.contains( d ); // Would have produced compile error

c.contains( base_ref ); // Would have produced compile error

编辑
对于认为这不是原因之一的怀疑者,这里是一个修改后的数组列表,其中包含一个将被泛化的 contains 方法

class MyCollection< E > extends ArrayList< E >
{
    public boolean myContains( E e )
    {
        return false;
    }
}

MyCollecttion< ? extends Base > c2 = ...;

c2.myContains( d ); // does not compile
c2.myContains( base_ref ); // does not compile

基本上

contains( Object o )
是一种让这个非常常见的用例与 Java 泛型一起使用的 hack。


0
投票

“那篮子苹果里有这个橙子吗?”

显然无法给出正确的答案。但这仍然留下了太多的可能性:

  1. 答案是假的。
  2. 问题格式不正确,不应该通过编译。

集合API选择了第一个。但第二个选择也很有意义。这样的问题99.99%都是废话,所以别问!


0
投票

Java 集合泛型类做出了合理的假设,即任何采用 类型参数作为输入的方法都被视为可变方法。并且在泛型方法的实现中禁止调用此类方法。但是

contains()
方法需要被视为不可变方法,并且它的调用应该在泛型方法的实现中成为可能。这就是为什么
contains()
方法接受
Object
作为输入,而不是键入 作为输入。

© www.soinside.com 2019 - 2024. All rights reserved.