Java的equals方法用来比较两个对象是否相等,该方法是继承自Object类,因此所有的类都可以使用该方法,但是一般类如果涉及到后面的比较的时候,都需要重写该方法,因为Object中提供的实现默认比较的是两个对象的地址。
(1)自反性,自己和自己比较,要返回true
(2)对称性,x和y比较的结果与y和x比较的结果应该保持一致
(3)传递性,x和y是相等的,y和z是相等的,那么x和z是相等的
(4)一致性,如果x和y引用对象的地址没有发生变化,那么反复调用equals应该返回一致的结果
(5)对于任意非空引用x,x.equals(null),应该返回false
public class Person {
private String name;
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object otherObject) {
// 如果两个对象是一个对象,那么返回true
// 满足自反性
if (this == otherObject) {
return true;
}
// 任何一个对象和null比较,都要返回false,满足性质5
if (otherObject == null) {
return false;
}
// 如果两个对象本质上类型不一样,那么应该返回false
// 为了满足对称性,这条语句保证了Person类的equals方法
// 仅能和同为Person类型的其他类进行比较
if (otherObject.getClass() != getClass()) {
return false;
}
// 如果所有的子类都有相同的比较语义,那么将上面的if判断改成
// 下面的
/*
if (!(otherObject instanceOf Person)) {
return false;
}*/
// 将Object强制类型转换为Person类型
Person other = (Person) otherObject;
// 这里使用Objects的静态方法进行比较
// 可以有效防止name为null,引发空指针异常的问题
return Objects.equals(other.name, name);
}
有的人建议将
if (o.getClass() != getClass()) {
return false;
}
换成
if (!(o instanceOf Person)) {
return false;
}
换了之后就可以让父类和子类进行比较了,但是这样造成一个麻烦:
(1)子类没有重写父类的equals方法,该方法可以让父类和子类进行比较,但是这样将会导致无法对子类特有的部分进行比较。
(2)子类重写了父类的equals方法,子类按照上述方法重写之后,将会丧失equals方法的对称性,父类和子类互换位置后比较结果可能不一致。
如果需要对子类特定的部分进行比较,那么不要使用instanceOf的方式,如果所有子类的比较规则都是相同的,那么可以使用instanceOf方法,然后在父类中声明该比较方法,同时将该方法声明为final类型,这样可以避免子类对该方法进行重写。equals的通用写法建议参考上述的模版写法。
重写equals的时候要同时重写对象的hashCode方法,equals相等的两个对象的hashCode也要返回相同的值,这是一个必须遵守的原则。