H A*_*ala 1 java serialization equals hashcode lombok
我有一个名为的类User.java,代码如下,
public abstract class User implements Serializable {
// serialVersionUID
private Integer userId;
private String userName;
private String fullName;
// Constructor()
// Getters and Setters
// equals()
// hashCode()
}
Run Code Online (Sandbox Code Playgroud)
而且我还有Contact.java课,
public class Contact extends User {
// serialVersionUID
private String phoneNumber;
private String address;
// Constructor()
// Getters and Setters
}
Run Code Online (Sandbox Code Playgroud)
所以我的问题是,即使生成了 User 类,equals我hashcode是否需要在子类 Contact 中再次重写它?
而且我正在使用IDE lombok,IDE是IntelliJ。我看到当我生成equals并hashcode通过 IDE 时,可以选择模板,例如,
生成时我发现生成的哈希码是不同的,例如,
lombok包含不同的代码Apache commons lang 3包含不同的代码那么它们之间有什么区别,每个生成的 hascode() 之间的区别是什么?
我可以尝试什么来解决这个问题?
通常是的,但首先您需要考虑平等在 User 对象的上下文中意味着什么。例如,我打赌您会认为 ID 相等的任何用户都是相等的,并且不需要或没有必要检查 或userName字段fullName或phoneNumber字段中的相等性。可能您只想检查 和或仅检查 和userName的组合。这与您要编写的代码无关,而是与您考虑的任意两个给定用户对象之间定义的相等关系有关。userIduserName
这个问题没有简单的答案。因此,我们首先来谈谈为什么要添加这样的方法。
这两种方法的目的是使您能够使用User类(或您的Contact类)的实例作为java.util.Map某种类型的键,或者将它们放入某些类型中java.util.List并使这些类型真正满足其文档告诉您的内容。如果您将没有 equals/hashCode impls 的类型的对象添加到 an 中,ArrayList然后调用.contains()它,它不会执行您期望的操作。如果您尝试将它们用作地图中的键,则它将无法正常工作。
因此,如果您不打算将 User 对象放入列表或地图中,则无需担心这些事情。根本不写方法。继续生活。
这让我们..
Contact 类的实例实际上代表什么?
根据您的答案, equals/hashCode 将会有很大不同。
对于基于 SQL 的数据库引擎,数据库引擎对相等性有一个清晰且普遍理解的定义。如果您说 Contact 的实例代表数据库中的一行,那么您的 java 代码中的相等定义必须与 SQL 的相等定义相匹配是合乎逻辑的,并且该定义很简单:相等主键?那么他们是平等的。
主键可以是任何内容,也可以是多列,但绝大多数数据库设计都使用自动生成的单个数字列作为主键,并且考虑到您有一个“userID”作为字段,这听起来像您的设计。
这意味着您想要的平等的定义是:平等意味着:相同的 userID。但情况会变得更糟:例如,在 hibernate 中,您可以创建 User 的实例并将该对象存在于 java 内存中,而无需将其保存到数据库中。这意味着(对于自动生成的主键),该对象实际上没有主键,而对于没有主键的 2 个对象,即使它们在所有方面都完全相同,这意味着它们不相等- 并且这是一个疯狂的相等定义(如果 userID 字段相等,则相等,除非 userID 字段表示指示“尚未保存到数据库”的占位符值,则不相等,即使它们在各个方面都相同),没有 auto - 生成的工具实现了这个,你必须自己编写它。
然后定义被有效地翻转:联系人条目有一个 id 是数据库的实现细节,并且是整个类中唯一不是联系人作为概念的实际固有部分的字段,因此平等可能是最好的定义为:“除了数据库 ID 之外,其他都相同,但这并不重要,因为它不是联系人的固有属性”。
同样,您需要在这里采取一些额外的操作;lombok、intellij、eclipse——所有这些工具都无法知道这些,并且默认情况下只会假设您打算仅在每个字段都相等时才使 2 个实例相等。
好吧,回到 equals/hashCode 的用途:使这些东西的实例充当映射和 forcontains等中的键,以便在这些实例存储在 java 列表中时给出正确的答案。Contact那么,如果您将 2 个单独的实例存储到一个列表中,并且它们都具有相同的 ID,但用户名值不同,您希望发生什么?根据您的回答,您知道这两种解释中哪一种是正确的。
他们不这样做。并不真地。hashCode 的要点很简单:
如果任意两个给定对象具有不同的哈希码,则它们不可能相等。
就是这样。这就是全部意思。具有相等哈希码的两个对象不需要相等(您必须调用a.equals(b)才能找出),但是具有不相等哈希码的两个对象不相等,并且不需要调用来a.equals(b)找出答案。这根本不是由java强制执行的,但是你应该这样写(确保如果两个对象有不相等的哈希码,它们不能相等)。如果你不这样做,你的类的实例在用作哈希图中的键时将会做出奇怪的事情。
有很多方法可以编写实现这种效果的算法。这解释了为什么存在微小差异。但是,它们都同样有效(它们为已知的不同对象生成不同的哈希码的效率大约相同,并且对于所有这些不同的工具,hashCode 方法的运行性能大约相同)。
就平等而言,子类型极其复杂。这是由于对象的 equals 和 hashCode javadoc 中记录的规则所致。这个答案已经很长了,所以我不会解释为什么,所以你只需要进行一些网络搜索或相信我的话。然而,要求工具在类型层次结构中自动生成相等/hashCode impls 是非常棘手的。
听起来您确实想锁定平等(以及哈希码)在该class User级别的含义(即:平等是通过具有相同的用户ID来定义的。如果这听起来更适合您的情况,也许是相同的用户名。但仅此而已),在这种情况下,您应该自己编写这些方法,它们应该是:
public final boolean equals(Object other) {
if (other == this) return true;
if (this.userId == null) return false;
if (!(other instanceof User) return false;
return this.userId.equals(((User) other).userId);
}
public final int hashCode() {
return userId == null ? System.identityHashCode() : 61 * userId.intValue();
}
Run Code Online (Sandbox Code Playgroud)
为什么?
a.equals(b)规则b.equals(a)。61?它只是一个任意选择的素数。几乎任何数字都可以;在特殊情况下,任意素数的效率非常非常高。然后使用lombok(免责声明:我是那里的核心贡献者,所以这是我对自己的评价),因为它具有最好的 equals 实现,并且您不必查看代码或维护它。使用@EqualsAndHashCode.Exclude注释标记您的 userId 字段,并使用 标记 Contact 类@EqualsAndHashCode(callSuper = true),使用@EqualsAndHashCode(或包含该内容的内容,例如@Value)标记 User - 需要 callSuper 来告诉 lombok 父类具有与 lombok 兼容的 equals 实现。
| 归档时间: |
|
| 查看次数: |
2179 次 |
| 最近记录: |