我有以下抽象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)是真实的
当我在子类内部调用超类的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);
}
即,由于使用this
运算符的超类中的方法,我不再需要明确检查当前对象(obj
)是否与instance of
属于同一类。 。
将实现放到超类的equals运算符中是否更健壮,还是更好的方法是使用getClass()
方法在子类中使用更明确的测试以符合合同规定? >
就hashCode()方法而言,我对专用于子类的私有实例字段进行哈希处理,然后将其简单地添加到超类中hash方法的结果中。我找不到任何文档来说明这是否是在一般层次结构或继承层次结构中实现hashCode()函数的正确方法。我看到了人们明确指定自己的哈希函数的代码。
对于我的问题过于笼统,我深表歉意,但我会尽力向他们提问,不要太含糊。
编辑:
我要求Intellij实现一个equals和hashcode方法,它决定采用我上面发布的最后一个实现。那么,在什么情况下我将在超类中使用instance of
?会在我在超类中实现最终的equals方法时(例如仅根据用户ID比较Person对象)吗?
答案 0 :(得分:1)
两个人可能有相同的id
吗?不应该这样因此该逻辑扩展到Employee
类,这意味着在equals
类中实现hashCode
和Person
就足够了。
在这一点上,由于您只处理int
,因此可以对Integer.hashCode(id)
使用hashCode
,而只需比较equals
的值。 / p>
答案 1 :(得分:1)
如果要实现equals和hashcode方法,请使用eclipse,只需在文件中单击鼠标右键,然后转到source并选择带有所需字段的generate equals()&hashcode(),如下所示:
答案 2 :(得分:1)
这是我阅读《有效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
)最后警告:
哈希码直接报价
对于对象中的每个重要字段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;
返回结果。
因此请遵循以下规则:
@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
应该已经可以唯一地标识任何人,因此您应该将其用于等值比较,而不要在任何子类中覆盖它。