Collections Java中为什么List.contains()方法的参数是Object?

3

java.util.List.contains(Object o)方法以Object为参数,并在内部使用Object.equals(Object o),如此处所述。

如果我在Netbeans中执行以下代码:

 List<String> listStr = new ArrayList<>();
 listStr.contains(34); //warning

它发出了明显的警告,即:
Given object can not contain instances of int (expected String)

因为所有人都能看到,String 永远不可能等于 int,所以为什么不应该将元素类型 E(在我的情况下是 String)作为参数,而不是使用 Object


1
或者,https://dev59.com/xnRA5IYBdhLWcg3wtwOe - 地图也是同样的情况。 - zapl
@thobens:我不认为这与泛型有关。 - Engineer2021
@thobens:恐怕不是这样。int 被封装成 Object。这不是泛型,而只是 Java 的机制。 - Engineer2021
请问楼主能否澄清一下? - thobens
1
与 https://dev59.com/DnVD5IYBdhLWcg3wDXJ3 密切相关,但可以说不是一个完全相同的问题。 - Raedwald
显示剩余4条评论
2个回答

1
我认为这是因为你可以做到这一点:
List<?> listStr = new ArrayList<>();

在这种情况下,您不知道类型是什么,因此如果将类型T作为参数而不是对象来验证元素是否包含在此列表中,这将是不可能的。这将破坏通配符的使用。

这不是回答这个问题。 - Engineer2021
我认为它回答了这个问题。我的意思是,这样你就不能使用通配符。 - luizcarlosfx
这不是这个问题的原因或关键所在,与此无关。 - Engineer2021
当然,这是问题的核心。他问为什么contains()应该接收一个对象而不是通用类型(“那么为什么它不应该将元素类型E作为参数而不是对象?”),这就是我回答的内容。 - luizcarlosfx
你能否取消这个踩的评价? - luizcarlosfx
显示剩余3条评论

1
严格来说,这样的实现是错误的。 原因是即使对象不属于类型E,它仍然可以在equals()调用时返回true。 假设你有一个像这样的类:
public class FakeString {
  private final String value;

  public FakeString(String value) {
    if (value == null) {
      throw new IllegalArgumentException();
    }
    this.value = value;
  }

  public int hashCode() {
    return value.hashCode();
  }

  public boolean equals(Object o) {
    return value.equals(o);
  }
}

那么这段代码会打印出true:
List<String> strings = Arrays.asList("foo", "bar", "baz");
System.out.println(strings.contains(new FakeString("bar")));

仅为澄清:这种行为是有意的,也是contains()使用Object而不是E的原因。顺便说一下,remove()也是如此。


你的int将会被包装成一个Object

(int) === 装箱 ===> (Integer) ==== 引用扩展 ===> (Object)

但是Netbeans很聪明,它会检测到这一点,并且注意到你的IntegerString不兼容。通常这会抛出一个ClassCastException。然而,javadoc清楚地说明在Collections.contains(Object)上抛出ClassCastException是可选的。

因此,在这种情况下,重载contains方法以允许Element类型没有任何好处,因为intfloatdouble等可以成功地自动装箱为Object。因此,只有Objectcontains是完全可以的。


出于未知原因,ListHashSetSetLinkedHashSet"如果指定元素的类型与此[插入类型]不兼容(可选)"时不会抛出ClassCastException异常。请注意可选部分。正如你所展示的,当你尝试传递一个不兼容的类型时,Netbeans会给出一个可疑的警告。这里的陷阱是如果你试图使用逻辑来依赖.contains(...)的返回值。一位没有注意到警告的开发人员可能会陷入假设contains总是有效的陷阱中,但实际上它并不是。

正如这个博客的开发者建议的那样,以下是避免将来掉入此陷阱的四个步骤:

谨慎编码和代码审查可能会导致开发人员或审阅者看到将不相关类的对象传递给contains方法。仅使用此形式的保护通常会让我感到不舒服。
使用现代版本的NetBeans或类似工具,可以标记“可疑”行为,非常有帮助。
单元测试结合代码覆盖测试,在识别出通过代码的看似奇怪流程方面非常有帮助,这些流程可以归因于本帖子中描述的问题之一。
至少会抛出ClassCastException的集合实现提供了运行时错误,以让开发人员知道正在执行不当操作。它可能不像编译时检测那样有效,但它确实使在没有它的情况下发生问题时更容易识别问题及其原因。然而,采用这种方法的主要缺点是,选择使用哪个集合实现通常是由重要考虑因素驱动的,这些考虑因素往往超过了希望具有运行时检测异常调用contains的愿望。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接