有时我会编写小脚本,以管理数据库中的记录或生成一些用于报告目的的数据。
大多数时候,我们使用Long
类型作为用户实体的 ID。如果我执行以下操作:
List<Long> listOfLong = Arrays.asList(1L, 2L, 3L);
System.out.println(listOfLong.contains(2));
它返回false
但为此:
System.out.println(integers.contains(2L));
它返回true
.
我们不应该为这样的事情得到编译时错误吗?
您没有编译错误的原因是Collection<E>
中contains
的签名定义为:
boolean contains(Object o)
没错,Object
. 不是contains(<E> o)
.
(他们为什么这样定义它? 我知道这是为了与 Java 5 之前的 Java 版本兼容,当时集合类型不是通用的。 如果他们在Java 5中重新定义了contains
方法,只允许<E>
参数,那么它将破坏许多在早期版本的Java中工作的代码。
因此,就编译器而言,在List<Long>
上调用contains
时,Integer
实例是合适的参数类型。
我们不应该为这样的事情得到编译时错误吗?
不。 它是有效的Java。
(如果你的意思是">应该"的意思是"如果"会更好......那我同意。 但是contains
这样定义是有原因的,没有回头路了。
可以做些什么来避免隐藏的错误
尝试使用静态代码分析器,如 FindBugs 或 PMD。 我不确定这些工具是否会检测到这个特定的错误,但它们可能会发现其他错误。
除此之外:
- 更多测试。
- 更改代码库以对 id 使用自定义类型。 一个不能自动装箱/取消装箱的。 (很多工作,治愈可能比疾病更糟糕......正如他们所说。
你问:
我们不应该为这样的事情得到编译时错误吗?
是的,我更喜欢这样,如果Java今天再次从头开始,也许会是这样。
如果这在我的代码中是一个严重的问题,我会为数字创建一个包装器,例如
class UserId {
public UserId(long id) { ... }
public long getId() { ... }
}
包含对对象类型参数使用等于方法的检查
boolean contains(Object o)
列表至少包含一个元素 e,以便
(o==null ? e==null : o.equals(e))
如果您不记得/确定您将在使用前将值设置为 Long
你可以先投到长,你可以使用Long.valueOf
List<Long> listOfLong = Arrays.asList(1l, 2l, 3l);
System.out.println(listOfLong.contains(Long.valueOf(2)));
或者将值 2 作为方法参数发送,以便在方法签名中为 Long
public void myMethod(Long number) {
List<Long> listOfLong = Arrays.asList(1l, 2l, 3l);
System.out.println(listOfLong.contains(number));
}
要添加另一个正确的答案:虽然这不是编译错误(因为 JLS 中的任何内容都没有违反(,但您的工具可能会引发警告。Eclipse 会告诉你:
不太可能的参数类型
int
用于Collection<Long>
上的contains(Object)
不太可能的参数类型
long
用于Collection<Integer>
上的contains(Object)