我应该对这个compareTo/equals/hashCode实现感到担忧吗?

3

我正在进行一些代码的QA测试,并发现开发人员有几个DTO实现了Comparable接口。这个DTO中有7或8个字段。compareTo方法只在一个字段上被实现:

private DateMidnight field1;  //from Joda date/time library

public int compareTo(SomeObject o) {
   if (o == null) {
      return -1;
   }
   return field1.compareTo(o.getField1());
}

同样,equals方法被重写并基本上归结为:

return field1.equals(o.getField1());

最后,hashcode方法的实现如下:

return field1.hashCode;

field1 不应该为 null,并且在这些对象中是唯一的(即我们不应该得到两个具有相同 field1 的对象)。

因此,实现是一致的,这很好,但我应该担心只使用一个字段吗?这是不寻常的吗?它可能会导致问题或使其他开发人员感到困惑吗?我考虑的情况是传递这些对象列表并且另一个开发人员使用 Map 或 Set 等数据结构时从这些对象获得异常行为。欢迎任何想法。谢谢!


离题一下...使用 Joda-Time 的用户应该注意,所涉及的字段是一种现已弃用且应避免使用的类型(DateMidnight)。现在适当的类型应该是 DateTime,必要时调用 withTimeAtStartOfDate - Basil Bourque
4个回答

5
我怀疑这是“先用者胜出”的情况——有人需要对这些对象的集合进行排序或将它们放入哈希映射中,而他们只关心日期。实现这个最简单的方法是覆盖equals/hashCode并按你所说的方式实现Comparable。
对于专业排序,更好的方法是在另一个类中实现Comparator...但不幸的是,Java没有任何等价的类来进行相等性测试。老实说,我认为这是Java集合的一个主要弱点。
假设这确实不是“唯一自然明显的比较”,从设计上来看,它肯定存在问题......应该非常仔细地记录。

1
是的,我认为“先使用者获胜”是一个很好的方式来描述这个设计,也是我对它感到略微不舒服的原因。你说得对,在这里使用Comparator似乎更合适。 - Chris Knight
2
@Chris:你感到不舒服是完全正确的。比较器更适合用于排序...只是很遗憾没有办法做到相同的等式:(在.NET中,有IEqualityComparer<T>用于此目的。) - Jon Skeet

2
严格来说,这违反了可比较规范:

http://download.oracle.com/javase/6/docs/api/java/lang/Comparable.html

请注意,null不是任何类的实例,即使e.equals(null)返回false,e.compareTo(null)应该抛出NullPointerException。
同样地,看起来equals方法在equals(null)上会抛出NPE而不是返回false(除非你已经将null处理代码“煮掉”)。
这可能会导致问题或者让其他开发人员感到困惑。这实际上取决于你的项目有多大,你的对象源代码被期望使用有多广泛/可重用/长寿:
小型/短寿/有限使用==可能不是问题。
大型/长寿/广泛使用==反直觉的实现可能会引起未来的问题。

2
是的,我确实已经“煮掉”了 equals 实现中的 null 处理代码,但感谢您指出 comparable 缺乏 null 指针异常处理。以前不知道这个。 - Chris Knight

2

如果field1确实是唯一的,你就不必担心它。但如果不是,可能会出现问题。无论如何,我的建议是进行一些单元测试。这些测试应该能够展示真相。


+1 对于单元测试(开发人员遗憾地没有为此编写任何显式的测试用例) - Chris Knight

1

我认为你不需要担心。三种方法之间的合同得到了保持,并且是一致的。

从业务逻辑的角度来看,它是否正确是一个不同的问题。

例如,如果field1映射到数据库中的主键,则完全有效。如果field1是人的“名字”,那么我会感到担忧。


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