我应该比较 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;
}
如您所见,我只比较 accountIdentifier
和 server
,不比较其他字段。我选择这种方法有几个原因。
- 我将帐户保存在
List
中。当用户更新帐户时,通过更改帐户名(这只是用户指定的名称来标识帐户)或注释,我可以accountList.set(accountList.indexOf(account), account);
更新列表中的帐户。如果 equals
比较所有属性,这种方法将不起作用,我必须解决它(例如通过遍历列表并手动检查这些属性)。
- 这实际上可能更重要,但我只是想了一会儿才想到的。
Account
由 accountIdentifier
和它所属的 server
唯一标识。用户可能决定重命名帐户或更改注释,但它仍然是同一个帐户。但是如果服务器改变了,我想我会认为它是一个不同的帐户。 id
只是一个内部 ID,因为帐户存储在数据库中。即使更改了,如果 accountIdentifier
和 server
保持不变,该帐户仍被视为同一帐户。
我想说的是,我基本上以这种方式实现 equals
以允许在应用程序的其余部分中使用更短、更简洁的代码。但是我不确定我是否违反了这里的一些规则,或者如果碰巧有人在使用我的应用程序 API.
,我是否正在做一些可能会导致其他开发人员头疼的事情
在equals
方法中只比较部分字段可以吗,还是应该比较所有字段?
这很好 - 可能是一件好事。如果您将相等性确定为 accountIdentifier
和 server
是不同且唯一的,那么这对您的用例完全有效。
您不想使用比您需要更多的字段,因为这会在您的代码中产生误报。这种方法非常适合您的需求。
您应该比较确定相等性所需的所有字段。如果 accountIdentifier
和 server
字段足以确定两个对象是否相等,那就完全没问题了。无需包括任何其他在平等方面无关紧要的字段。
答案是"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。 类 是 EqualsBuilder 和 HashCodeBuilder。两者都允许您连接要在比较中使用的字段,并且还可以使用反射。
我正在开发一个允许用户管理帐户的应用程序。所以,假设我有一个 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;
}
如您所见,我只比较 accountIdentifier
和 server
,不比较其他字段。我选择这种方法有几个原因。
- 我将帐户保存在
List
中。当用户更新帐户时,通过更改帐户名(这只是用户指定的名称来标识帐户)或注释,我可以accountList.set(accountList.indexOf(account), account);
更新列表中的帐户。如果equals
比较所有属性,这种方法将不起作用,我必须解决它(例如通过遍历列表并手动检查这些属性)。 - 这实际上可能更重要,但我只是想了一会儿才想到的。
Account
由accountIdentifier
和它所属的server
唯一标识。用户可能决定重命名帐户或更改注释,但它仍然是同一个帐户。但是如果服务器改变了,我想我会认为它是一个不同的帐户。id
只是一个内部 ID,因为帐户存储在数据库中。即使更改了,如果accountIdentifier
和server
保持不变,该帐户仍被视为同一帐户。
我想说的是,我基本上以这种方式实现 equals
以允许在应用程序的其余部分中使用更短、更简洁的代码。但是我不确定我是否违反了这里的一些规则,或者如果碰巧有人在使用我的应用程序 API.
在equals
方法中只比较部分字段可以吗,还是应该比较所有字段?
这很好 - 可能是一件好事。如果您将相等性确定为 accountIdentifier
和 server
是不同且唯一的,那么这对您的用例完全有效。
您不想使用比您需要更多的字段,因为这会在您的代码中产生误报。这种方法非常适合您的需求。
您应该比较确定相等性所需的所有字段。如果 accountIdentifier
和 server
字段足以确定两个对象是否相等,那就完全没问题了。无需包括任何其他在平等方面无关紧要的字段。
答案是"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。 类 是 EqualsBuilder 和 HashCodeBuilder。两者都允许您连接要在比较中使用的字段,并且还可以使用反射。