重写equals方法

193 阅读4分钟

equals方法的特性

  1. 自反性,x.equals(x)应该返回true
  2. 传递性
  3. 一致性:如果x、y没有发生变化,那么反复调用equals应该返回同样的结果
  4. 对称性:x.equals(y) == y.equals(x)
  5. 对于任意非空引用x,调用x.equals(null)返回false

equals方法实现的策略

如果子类拥有自己的相等概念,则对称性需求将强制采用getClass进行检测

意思是如果是否相等由子类自己决定,那么在判断两个对象是否相等的第一步——判断两个对象是否属于相同的类,将采用getClass方法检测。getClass方法返回的是一个Class对象。对于某个类不管它有多少个对象,这些对象都会对应与同一个Class(这个Class对象是JVM生成的)。所以说如果两个对象属于同一个类,那么他们调用getClass方法返回的Class对象会是同一个对象。所以可以直接用等号判断返回的两个Class对象是否相等。

如果由超类决定相等的概念,那么就可以使用instanceof来检测,这样可以在不同的子类的对象之间进行比较

  • 用instanceof检测对象的类型,其实就相当于is-a的意思。m instanceof Manager——m is-a Manager,如果m是Manager的对象,那么自然会返回true。但根据继承的概念子类和超类也是is-s的关系,子类是个超类,所以如果m是Manager的子类的对象,instanceof也会返回true。

  • 所以在相等概念由超类决定的前提下,只要这个对象是超类或超类子类的对象就允许继续比较,因为既然它至少属于超类就保证了它拥有超类所有的特征,而相等概念由超类决定,所以是可以比较的。

  • 由超类决定相等概念的情况下,超类的equals方法应该的是final的

实现一个完美的equals方法的建议

  1. 显式参数命名为otherObject,稍后将其转换成另一个叫做other的变量
  2. 检测this与otherObject是否为同一对象的引用 if(this==otherObject)return true;
  1. 检测otherObject是否为null if (otherObject == null) return false;
  1. 比较this与otherObject是否属于同一个类 如果equals语义在每个子类中有所变化——相等的概念由子类决定,那么就要严格的判断是否属于同一个类,用getClass来判断 if (this.getClass() != otherObject.getClass()) return false; 如果所有的子类拥有统一的语义——相同的概念由超类决定,那么用isntanceof来判断 if (!otherObject isntanceof ClassName) return false
  1. 将otherObject转换为相应的类类型变量——若相等概念是由超类决定的,那么装换成超类(也没办法直到是哪个子类,也没必要转换到子类,因为这种情况下equals函数应该在超类中,而且应该是final的),若由子类决定,那么就应该是当前实现的类 ClassName other = (ClassName)otherObject;
  1. 到了这一步可以确定两个对象是可以在他们所规定的相等概念上进行被比较了——使用equals比较所有的对象域,基本类型域直接使用==比较,所有域都匹配返回true,否则返回false

Employee-Manager的equals方法实现,

相等语义由子类决定
  1. Employee
public class Employee {
    private String name;
    private double salsry;
    private LocalDate hireDay;

    /** 省略getter、setter**/
    
    public boolean equals(Object otherObject){

        if (this == otherObject) return true;
        if (otherObject == null) return false;
        if (this.getClass() != otherObject.getClass()) return false;

        Employee employee = (Employee)otherObject;

        return this.name.equals(employee.name)
                && this.salsry == employee.salsry
                && this.hireDay.equals(employee.hireDay);
    }
}

  1. Manager
public class Manager extends Employee {

    private double bonus;

    public double getSalary(){
        double baseSalary = super.getSalsry();
        return baseSalary + bonus;
    }
    
    /*省略bouns的getter、setter*/

    @Override
    public boolean equals(Object o) {
        
        if (!super.equals(o)) return false;
        Manager manager = (Manager) o;
        return Double.compare(manager.bonus, bonus) == 0;
    }
}

相等概念由超类决定
  • 假设雇员都有一个员工ID,ID相等就是同一个人,所以这种情况下的equals在Employee中实现成final即可
public class Employee extends Person {
    
    private int id;
    
    /*其他域、geter、setter、构造方法省略*/

    public final boolean equals(Object o){
        if (this == o) return true;
        if (o == null || !(o instanceof Employee)) return false;
        Employee e = (Employee)o;
        return this.id == e.id;
    }
}

ps:equals方法的两种实现都可以在IDEA中自动生成,hashcode也是