我应该比较 class 的 "equals" 方法中的所有字段吗?

Should I compare all fields in my class's "equals" method?

我正在开发一个允许用户管理帐户的应用程序。所以,假设我有一个 Account class,代表用户的帐户之一:

class Account
{
    public int id;
    public String accountName;
    public String accountIdentifier;
    public String server;
    public String notes;
}

我的 equals 方法如下所示:

public boolean equals(Object o)
{
    if (this == o)
        return true;
    if (o == null || !(o instanceof Account))
        return false;

    Account other = (Account) o;
    if (!accountIdentifier.equals(other.accountIdentifier))
        return false;
    if (!server.equals(other.server))
        return false;
    return true;
}

如您所见,我只比较 accountIdentifierserver,不比较其他字段。我选择这种方法有几个原因。

  1. 我将帐户保存在 List 中。当用户更新帐户时,通过更改帐户名(这只是用户指定的名称来标识帐户)或注释,我可以accountList.set(accountList.indexOf(account), account);更新列表中的帐户。如果 equals 比较所有属性,这种方法将不起作用,我必须解决它(例如通过遍历列表并手动检查这些属性)。
  2. 这实际上可能更重要,但我只是想了一会儿才想到的。 AccountaccountIdentifier 和它所属的 server 唯一标识。用户可能决定重命名帐户或更改注释,但它仍然是同一个帐户。但是如果服务器改变了,我想我会认为它是一个不同的帐户。 id 只是一个内部 ID,因为帐户存储在数据库中。即使更改了,如果 accountIdentifierserver 保持不变,该帐户仍被视为同一帐户。

我想说的是,我基本上以这种方式实现 equals 以允许在应用程序的其余部分中使用更短、更简洁的代码。但是我不确定我是否违反了这里的一些规则,或者如果碰巧有人在使用我的应用程序 API.

,我是否正在做一些可能会导致其他开发人员头疼的事情

equals方法中只比较部分字段可以吗,还是应该比较所有字段?

这很好 - 可能是一件好事。如果您将相等性确定为 accountIdentifierserver 是不同且唯一的,那么这对您的用例完全有效。

您不想使用比您需要更多的字段,因为这会在您的代码中产生误报。这种方法非常适合您的需求。

您应该比较确定相等性所需的所有字段。如果 accountIdentifierserver 字段足以确定两个对象是否相等,那就完全没问题了。无需包括任何其他在平等方面无关紧要的字段。

答案是"it depends depends on the semantics of your data"。

例如,您可能会在内部存储一个可以从其他字段派生(计算)的字段。在这种情况下,您不需要比较计算值。

总的来说,任何不能从其他领域推导出来的东西都应该包括在内。

是的,这样做绝对没问题。 可以决定相等性对您的 class 意味着什么,并且您应该以对您的应用程序逻辑最有意义的方式使用它——特别是对于集合和其他这样的 classes 使用了平等。听起来您已经考虑过了,并决定 (server, identifier) 对是唯一区分实例的东西。

这意味着,例如,具有相同 (server, identifier) 对但不同 accountName 的两个实例是同一帐户的不同版本,并且可能需要以某种方式解决差异;这是一个完全合理的语义。

定义一个单独的 boolean allFieldsEqual(Account other) 方法来覆盖 "extended" 定义可能是有意义的,这取决于您是否需要它(或者会发现它对测试有用)。

而且,当然,您应该使用您使用的 equals 的任何定义覆盖 hashCode to make it consistent

对于key,一般应该使用business key,这个key可以是simple key,也可以是composite key,不一定需要包含实体中的所有字段。所以...取决于每个案例 select 什么识别一个实体。如果可能,应该是最少的字段数,可以完整且唯一地标识实体。

有些人更喜欢(这是一个很好的做法)创建一个代理键来标识对象,当你想使用任何 ORM 持久化你的对象时,这非常有用,因为你不需要导出键1:M 或 M:N 关系中的子实体。例如,如果您将样本中的 ID 创建为内部唯一标识符,则可以将其视为代理键。

还可以考虑:

  • 你总是覆盖 equals 你也必须覆盖 hashCode,这对于像 Collections 这样的 类 正常工作很重要,地图等
  • Apache 提供了一个非常好的 API 来帮助实现 equals 和 hashCode。 类 是 EqualsBuilderHashCodeBuilder。两者都允许您连接要在比较中使用的字段,并且还可以使用反射。