java.util.List.contains(Object o)
方法将Object
作为参数,并在here内部使用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
(在我的case String
)作为参数而不是Object
?
答案 0 :(得分:1)
我认为这是因为你可以这样做:
List<?> listStr = new ArrayList<>();
在这种情况下,您不知道什么是类型,因此如果将类型T作为参数而不是对象,则无法验证此列表中是否包含元素。这会打破使用通配符。
答案 1 :(得分:1)
严格来说,这样的实施是错误的。
这样做的原因是,即使某个对象不属于E
类型,它仍然可以在true
来电时返回equals()
。
假设你有一个这样的课:
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) === boxing ===> (Integer) ==== reference widening ===> (Object)
但Netbeans很聪明并且发现了这一点,并注意到您的Integer
与String
不兼容。通常这会抛出ClassCastException
。但是,javadoc明确指出在Collections.contains(Object)上抛出ClassCastException是可选的。
因此,在这种情况下,重载contains
方法以允许Element
类型没有任何好处,因为int
,float
,double
,等可以成功自动装箱到Object
。因此,只有contains
的{{1}}就可以了。
<小时/>
如果原因不明,Object
,List
,HashSet
和Set
如果 &#34则不会抛出LinkedHashSet
; f指定元素的类型与此[在此处插入类型](可选)&#34; 不兼容。请注意 可选 部分。正如您所示,当您尝试传递不兼容的类型时,Netbeans会向您发出可疑警告。这里的陷阱是,如果您尝试使用逻辑来依赖ClassCastException
的返回值。一个没有注意到他的警告的开发人员可能陷入假设.contains(...)
总是 工作 的陷阱,但它没有&#39 ;吨
正如此blog建议的开发人员所说,以下是避免此陷阱的四个步骤:
仔细编码和代码审查可能会导致开发人员或审阅者发现无关类的对象正在通过 到contains方法。离开时我通常会感到不舒服 只是这种保护形式。
使用现代版本的NetBeans或类似工具来标记&#34;可疑&#34;行为可能非常有帮助。
单元测试与代码覆盖率测试相结合有助于识别可能存在的代码看似奇怪的流程 归因于本文所述的问题。
- 醇>
抛出ClassCastException的集合实现至少会提供运行时错误,让开发人员知道不正确 正在采取行动。它可能没有编译时那么有效 检测,但它确实使识别有一个更容易 问题以及什么时候发生它没有它。然而 这种方法的主要缺点是选择哪种 要使用的集合实现通常是重要的 考虑因素往往超过了运行时的愿望 检测到包含的错误调用。