[英]What is the proper way to implement a robust equals() and hashCode() method in an inheritance hierarchy?
我有以下抽象的Person
类:
import java.util.Objects;
public abstract class Person {
protected String name;
protected int id;
public Person(String name, int id) {
this.name = name;
this.id = id;
}
public abstract String description();
@Override
public boolean equals(Object obj) {
if(this == obj) return true;
if(!(obj instanceof Person)) return false;
return Objects.equals(this.name, ((Person) obj).name) &&
this.id == ((Person) obj).id;
}
@Override
public int hashCode() {
return Objects.hash(this.name, this.id);
}
}
现在我有一个Person
的子类叫做Employee
:
import java.time.LocalDate;
import java.util.Objects;
public class Employee extends Person {
private double salary;
private LocalDate hireDay;
public Employee(String name, int id, double salary, int year, int month, int day) {
super(name, id);
this.salary = salary;
this.hireDay = LocalDate.of(year, month, day);
}
@Override
public String description() {
return "Employee with a salary of " + this.salary;
}
@Override
public int hashCode() {
return super.hashCode() + Objects.hash(this.salary,this.hireDay);
}
@Override
public boolean equals(Object obj) {
return super.equals(obj) &&
Double.compare(this.salary, ((Employee) obj).salary) == 0
&& Objects.equals(this.hireDay,((Employee)obj).hireDay);
}
要正确实现equals方法,它必须符合以下合同。
反身:x.equals(x)始终为True
对称的:x.equals(y)等效于y.equals(x)
传递式:x.equals(y)和y.equals(z)表示x.equals(z)为true
当我在子类内部调用超类的equals()方法时,首先要确保所有要比较的对象都是超类的子类。 此问题解决了比较混合类型的问题,并照顾了上述合同。 我不再需要使用以下equals实现:
@Override
public boolean equals(Object obj) {
if(this == obj) return true;
else if(obj == null || this.getClass() != obj.getClass()) return false;
Employee other = (Employee) obj;
return Objects.equals(this.name, other.name) &&
Double.compare(this.salary, other.salary) == 0 &&
Objects.equals(this.hireDay, other.hireDay);
}
即,由于使用了operator instance of
的超类中的方法,我不再必须明确检查当前对象( this
)是否与obj
属于同一类。
将实现放到超类的equals运算符中是否更健壮,还是要使用子类中通过getClass()
方法进行更明确的测试以符合合同,这会更好吗?
根据hashCode()方法,我对特定于子类的私有实例字段进行哈希处理,然后将其简单地添加到超类中hash方法的结果中。 我找不到任何文档来说明这是否是在一般层次结构或继承层次结构中实现hashCode()函数的正确方法。 我看过人们明确指定自己的哈希函数的代码。
如果我的问题过于笼统,我深表歉意,但我尽量不问得太清楚。
编辑:
我要求Intellij实现一个equals和hashcode方法,它决定采用我上面发布的最后一个实现。 那么,在什么情况下我将在超类中使用的instance of
? 是在超类中实现最终的equals方法时(例如仅基于用户ID比较Person对象)吗?
两个人可能拥有相同的id
吗? 不应该这样 因此该逻辑扩展到Employee
类,这意味着在Person
类中实现equals
和hashCode
就足够了。
在这一点上,由于您只处理int
,因此可以将Integer.hashCode(id)
用作hashCode
而只需比较equals
的值即可。
这是我从阅读《有效的Java第二版》中获得的笔记:
等于必须遵守一般合同:
non-null x
: x.equals(x) == true
non-null x,y
: x.equals(y) <==> y.equals(x)
non-null x,y,z
: x.equals(y) and y.equals(z) ==> x.equals(z) == true
x.equals(y) == true
,则如果x
和y
没有变化,则所有调用都必须返回true x
: x.equals(null) == false
高质量等于方法:
x == x
) null
) 最后警告:
有效Java 2nd Edition中的Hashcode直接引用
对于对象中的每个有效字段f
(即equals方法考虑的每个字段),执行以下操作:
(f ? 1 : 0)
。 byte, char, short, or int, compute (int) f.
long, compute (int) (f ^ (f >>> 32)).
float, compute Float.floatToIntBits(f).
double, compute Double.doubleToLongBits(f)
,然后对所得的long
进行哈希处理。 equals
,则对该字段进行递归调用hashCode
来比较该字段。 如果需要更复杂的比较,请为此字段计算一个“规范表示”,并在规范表示上调用hashCode。 如果该字段的值为null
,则return 0
(或其他常数,但是0是传统的)。 result = 31 * result + c;
: result = 31 * result + c;
返回结果。
因此,请遵循以下规则:
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Employee)) {
return false;
}
Employee other = (Employee) obj;
return super.equals(other) &&
Double.compare(this.salary, other.salary) == 0 &&
this.hireDay.equals(other.hireDay);
}
在您的情况下,尽管id
似乎已经可以唯一地标识任何人,所以您应该仅在id
使用它进行比较,而不要在任何子类中覆盖它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.