使用 Eclipse 生成 equals() 和 hashCode()
上次更新:2024年1月25日
1. 简介
在本文中,我们将探讨使用 Eclipse IDE 生成 equals() 和 hashCode() 方法。我们将展示 Eclipse 代码自动生成功能的强大和便利性,并强调仍然需要对代码进行严格的测试。
2. 规则
Java 中的 equals() 用于检查 2 个对象是否相等。一个好的测试方法是确保对象具有对称性、自反性和传递性。也就是说,对于三个非空对象 a、b 和 c
- 对称性 – a.equals(b) 当且仅当 b.equals(a)
- 自反性 – a.equals(a)
- 传递性 – 如果 a.equals(b) 且 b.equals(c),则 a.equals(c)
hashCode() 必须遵守一条规则
- equals() 相等的 2 个对象必须具有相同的 hashCode() 值
3. 包含基本类型的类
让我们考虑一个仅包含基本类型成员变量的 Java 类
public class PrimitiveClass {
private boolean primitiveBoolean;
private int primitiveInt;
// constructor, getters and setters
}
我们使用 Eclipse IDE 通过“Source->Generate hashCode() and equals()”生成 equals() 和 hashCode()。 Eclipse 提供了一个如下所示的对话框
我们可以通过选择“Select All”(全选)来确保包含所有成员变量。
请注意,插入点下方的选项会影响生成的代码风格。 在这里,我们不选择任何这些选项,选择“OK”,方法就会被添加到我们的类中
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (primitiveBoolean ? 1231 : 1237);
result = prime * result + primitiveInt;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
PrimitiveClass other = (PrimitiveClass) obj;
if (primitiveBoolean != other.primitiveBoolean) return false;
if (primitiveInt != other.primitiveInt) return false;
return true;
}
生成的 hashCode() 方法首先声明一个质数 (31),对基本类型对象执行各种操作,并根据对象的状态返回其结果。
equals() 首先检查两个对象是否是同一个实例 (==),如果是,则返回 true。
接下来,它检查比较对象是否非空,并且两个对象是否是同一类,如果不是,则返回 false。
最后,equals() 检查每个成员变量的相等性,如果任何一个不相等,则返回 false。
因此,我们可以编写简单的测试
PrimitiveClass aObject = new PrimitiveClass(false, 2);
PrimitiveClass bObject = new PrimitiveClass(false, 2);
PrimitiveClass dObject = new PrimitiveClass(true, 2);
assertTrue(aObject.equals(bObject) && bObject.equals(aObject));
assertTrue(aObject.hashCode() == bObject.hashCode());
assertFalse(aObject.equals(dObject));
assertFalse(aObject.hashCode() == dObject.hashCode());
4. 包含集合和泛型的类
现在,让我们考虑一个更复杂的 Java 类,其中包含集合和泛型
public class ComplexClass {
private List<?> genericList;
private Set<Integer> integerSet;
// constructor, getters and setters
}
再次使用 Eclipse “Source->Generate hashCode() and equals()”。 请注意,hashCode() 使用 instanceOf 来比较类对象,因为我们在 Eclipse 对话框的选项中选择了“Use ‘instanceof’ to compare types”(使用‘instanceof’比较类型)。 我们得到
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((genericList == null)
? 0 : genericList.hashCode());
result = prime * result + ((integerSet == null)
? 0 : integerSet.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (!(obj instanceof ComplexClass)) return false;
ComplexClass other = (ComplexClass) obj;
if (genericList == null) {
if (other.genericList != null)
return false;
} else if (!genericList.equals(other.genericList))
return false;
if (integerSet == null) {
if (other.integerSet != null)
return false;
} else if (!integerSet.equals(other.integerSet))
return false;
return true;
}
生成的 hashCode() 方法依赖于 Java 核心方法 AbstractList.hashCode() 和 AbstractSet.hashCode()。 这些方法遍历集合,对每个项目的 hashCode() 值求和,并返回结果。
类似地,生成的 equals() 方法使用 AbstractList.equals() 和 AbstractSet.equals(),这些方法通过比较字段来比较集合的相等性。
我们可以通过测试一些例子来验证其健壮性
ArrayList<String> strArrayList = new ArrayList<String>();
strArrayList.add("abc");
strArrayList.add("def");
ComplexClass aObject = new ComplexClass(strArrayList, new HashSet<Integer>(45,67));
ComplexClass bObject = new ComplexClass(strArrayList, new HashSet<Integer>(45,67));
ArrayList<String> strArrayListD = new ArrayList<String>();
strArrayListD.add("lmn");
strArrayListD.add("pqr");
ComplexClass dObject = new ComplexClass(strArrayListD, new HashSet<Integer>(45,67));
assertTrue(aObject.equals(bObject) && bObject.equals(aObject));
assertTrue(aObject.hashCode() == bObject.hashCode());
assertFalse(aObject.equals(dObject));
assertFalse(aObject.hashCode() == dObject.hashCode());
5. 继承
让我们考虑使用继承的 Java 类
public abstract class Shape {
public abstract double area();
public abstract double perimeter();
}
public class Rectangle extends Shape {
private double width;
private double length;
@Override
public double area() {
return width * length;
}
@Override
public double perimeter() {
return 2 * (width + length);
}
// constructor, getters and setters
}
public class Square extends Rectangle {
Color color;
// constructor, getters and setters
}
如果我们尝试在 Square 类上执行“Source->Generate hashCode() and equals()”,Eclipse 会警告我们“超类 ‘Rectangle’ 没有重新声明 equals() 和 hashCode():生成的代码可能无法正常工作”。
同样,当我们尝试在 Rectangle 类上生成 hashCode() 和 equals() 时,会收到关于超类 ‘Shape’ 的警告。
Eclipse 允许我们尽管有警告继续操作。 在 Rectangle 的情况下,它继承了一个抽象 Shape 类,该类无法实现 hashCode() 或 equals(),因为它没有具体的成员变量。 在这种情况下,我们可以忽略 Eclipse。
Square 类,然而,从 Rectangle 继承了 width 和 length 成员变量,以及它自己的颜色变量。在没有首先为 Rectangle 实现 hashCode() 和 equals() 的情况下,在 Square 中创建它们意味着在 equals()/hashCode() 中仅使用 color。
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((color == null) ? 0 : color.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
Square other = (Square) obj;
if (color == null) {
if (other.color != null)
return false;
} else if (!color.equals(other.color))
return false;
return true;
}
快速测试表明,如果仅 width 不同,Square 的 equals()/hashCode() 是不够的,因为 width 没有包含在 equals()/hashCode() 计算中。
Square aObject = new Square(10, Color.BLUE);
Square dObject = new Square(20, Color.BLUE);
Assert.assertFalse(aObject.equals(dObject));
Assert.assertFalse(aObject.hashCode() == dObject.hashCode());
让我们通过使用 Eclipse 为 Rectangle 类生成 equals()/hashCode() 来修复这个问题。
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
long temp;
temp = Double.doubleToLongBits(length);
result = prime * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(width);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
Rectangle other = (Rectangle) obj;
if (Double.doubleToLongBits(length)
!= Double.doubleToLongBits(other.length)) return false;
if (Double.doubleToLongBits(width)
!= Double.doubleToLongBits(other.width)) return false;
return true;
}
我们必须重新生成 Square 类中的 equals()/hashCode(),以便调用 Rectangle 的 equals()/hashCode()。 在此代码生成过程中,我们选择了 Eclipse 对话框中的所有选项,因此我们看到注释、instanceOf 比较和 if 块。
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((color == null) ? 0 : color.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (!(obj instanceof Square)) {
return false;
}
Square other = (Square) obj;
if (color == null) {
if (other.color != null) {
return false;
}
} else if (!color.equals(other.color)) {
return false;
}
return true;
}
重新运行上面的测试,我们现在通过了测试,因为 Square 的 hashCode()/equals() 计算正确。
6. 结论
Eclipse IDE 非常强大,允许自动生成样板代码——getter/setter、各种类型的构造函数、equals() 和 hashCode()。
通过理解 Eclipse 在做什么,我们可以减少在这些编码任务上花费的时间。但是,我们仍然必须小心并使用测试来验证我们的代码,以确保我们处理了所有预期的用例。
支持本文的代码可在 GitHub 上获取。 一旦你以 Baeldung Pro 会员 身份登录,就开始学习并在项目上进行编码。















