在使用记录时,可以做什么来避免由于自动装箱而导致的隐藏错误

what can be done to avoid following hidden mistake due to autoboxing while working with Records

有时我会编写一些小脚本,以管理数据库中的记录或生成一些用于报告目的的数据。

大多数时候我们使用 Long 类型作为用户实体的 ID。如果我执行以下操作:

List<Long> listOfLong = Arrays.asList(1L, 2L, 3L);
System.out.println(listOfLong.contains(2));

它 returns false 但为此:

System.out.println(integers.contains(2L));

它returns true.

我们不应该得到这样的编译时错误吗?

contains 使用 equals 方法检查对象类型参数

 boolean contains(Object o)

list contains at least one element e such that (o==null ? e==null : o.equals(e))

如果你不这样做 remember/sure 你将在使用它之前将值设置为 Long

可以先投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));
}

您问的是:

Shouldn't we get a compile time error for such thing?

是的,如果 Java 今天再次从头开始,我更愿意那样做。

如果这在我的代码中是一个严重的问题,我会为该数字创建一个包装器,例如

class UserId {
  public UserId(long id) { ... }
  public long getId() { ... }
}

你没有编译错误的原因是 Collection<E>contains 的签名定义为:

boolean contains(Object o)

没错,Object。不是 contains(<E> o)

(他们为什么这样定义它?我知道这是为了与 Java 5 之前的 Java 版本兼容,当时集合类型不是通用的。如果他们重新定义了Java 5 中的 contains 方法只允许 <E> 个参数,它会破坏很多在早期版本 Java 中工作的代码。)

因此,就编译器而言,在 List<Long>.

上调用 contains 时,Integer 实例是合适的参数类型

Shouldn't we get a compile time error for such thing ?

没有。有效 Java。

(如果你的意思是 "should" 在 "it would be better if" 的意义上......那么我同意。但是 contains 被这样定义是有原因的,并且没有回头路.)

What can be done to avoid the hidden mistake

尝试使用 FindBugs 或 PMD 等静态代码分析器。我不确定这些工具是否会检测到这个特定错误,但它们可能会发现其他错误。

除此之外:

  • 更多测试。
  • 更改您的代码库以使用自定义类型的 ID。一个不能自动装箱/拆箱的。 (很多工作,治愈可能比疾病更糟糕......正如他们所说。)

补充另一个正确的答案:虽然这不是编译错误(因为没有违反 JLS 中的任何内容),但您的工具可能会发出警告。 Eclipse 会告诉你:

Unlikely argument type int for contains(Object) on a Collection<Long>

Unlikely argument type long for contains(Object) on a Collection<Integer>