严格来说,这样的实现是错误的。
原因是即使对象不属于类型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很聪明,它会检测到这一点,并且注意到你的Integer
与String
不兼容。通常这会抛出一个ClassCastException
。然而,javadoc清楚地说明在Collections.contains(Object)上抛出ClassCastException是可选的。
因此,在这种情况下,重载contains
方法以允许Element
类型没有任何好处,因为int
、float
、double
等可以成功地自动装箱为Object
。因此,只有Object
的contains
是完全可以的。
出于未知原因,
List
、
HashSet
、
Set
和
LinkedHashSet
在
"如果指定元素的类型与此[插入类型]不兼容(可选)"时不会抛出
ClassCastException
异常。请注意
可选部分。正如你所展示的,当你尝试传递一个不兼容的类型时,Netbeans会给出一个可疑的警告。这里的陷阱是如果你试图使用逻辑来依赖
.contains(...)
的返回值。一位没有注意到警告的开发人员可能会陷入假设
contains
总是
有效的陷阱中,但实际上它并不是。
正如这个博客的开发者建议的那样,以下是避免将来掉入此陷阱的四个步骤:
谨慎编码和代码审查可能会导致开发人员或审阅者看到将不相关类的对象传递给contains方法。仅使用此形式的保护通常会让我感到不舒服。
使用现代版本的NetBeans或类似工具,可以标记“可疑”行为,非常有帮助。
单元测试结合代码覆盖测试,在识别出通过代码的看似奇怪流程方面非常有帮助,这些流程可以归因于本帖子中描述的问题之一。
至少会抛出ClassCastException的集合实现提供了运行时错误,以让开发人员知道正在执行不当操作。它可能不像编译时检测那样有效,但它确实使在没有它的情况下发生问题时更容易识别问题及其原因。然而,采用这种方法的主要缺点是,选择使用哪个集合实现通常是由重要考虑因素驱动的,这些考虑因素往往超过了希望具有运行时检测异常调用contains的愿望。
int
被封装成Object
。这不是泛型,而只是 Java 的机制。 - Engineer2021