Java 基础 - 如何重写equals()

ref:https://www.cnblogs.com/TinyWalker/p/4834685.html

--------------------

编写equals方法的建议:

  1. 显示参数命名为otherObject, 稍后转化成other变量
    public boolean equals(Object otherObject)
  2. 检测this和otherObject是否是同一个对象的引用,是,返回true;
    if(this==otherObject){
        return true;
    }
  3. 检测otherObject是否为null, 是, 返回false;
    if(otherObject == null){
        return false;
    }
  4. 比较this和otherObject是否属于同一个类. 如果equals的语义在每个子类中有所改变,就使用getClass检测:
    if(getClass() != otherObject.getClass()){
        return false;
    }
    如果子类语义相同,使用instanceof检测:
    if(!(otherObject instanceof Employee)){
        return false;
    }
  5. 将otherObject转化为相对应的类型变量other
    Employee other = (Employee)otherObject;
  6. 对所需要的比较的数据域进行比较. 如果是基本数据类型,使用a==b比较; 如果是对象比较,调用Objects.equals(a, b)进行比较
    return Objects.equals(name, other.name) && salary == other.salary && Objects.equals(hireDay, other.hireDay);

整个流程可以参照例1;

例1:雇员对象比较

如果两个雇员对象的姓名, 薪水和雇佣一样,就认为它们相等.重写equals方法如下:

复制代码
public class Employee {

    private String name;
    private double salary;
    private Date hireDay;

    ... 

    @Override
    public boolean equals(Object obj) {
        // 如果为同一对象的不同引用,则相同
        if (this == obj) {
            return true;
        }
        // 如果传入的对象为空,则返回false
        if (obj == null) {
            return false;
        }

        // 如果两者属于不同的类型,不能相等
        if (getClass() != obj.getClass()) {
            return false;
        }

        // 类型相同, 比较内容是否相同
        Employee other = (Employee) obj;

        return Objects.equals(name, other.name) && salary == other.salary && Objects.equals(hireDay, other.hireDay);
    }

复制代码

  注意,比较通过Objects中静态函数equals比较两个对象是否相等.该方法源码如下:

 public static boolean equals(Object a, Object b) {
        return (a == b) || (a != null && a.equals(b));
    }

 这样,当两个对象都为null时,返回true,例如,两个Employee对象的name都为null, 返回true; 如果第一个对象不为null,则调用a.equals(Object obj)方法

常见equals方法实现错误

1-未使用@override对覆盖超类的方法进行标记.

复制代码
public class Employee {
    public boolean equals(Employee other) {


    return Objects.equals(name, other.name) && salary ==other.salary && Objects.equals(hireDay, other.hireDay);
}
复制代码

这个方法声明的显示参数类型是Employee. 其结果并没有覆盖Object中的equals方法,而是定义了一个完全无关的方法.为了避免发生类型错误,可以使用@override对覆盖超类的方法进行标记.

2-没有同时override hashcode()函数 

object对象中的 public boolean equals(Object obj),对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象时,此方法才返回 true;
注意:当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。如下:
(1)当obj1.equals(obj2)为true时,obj1.hashCode() == obj2.hashCode()必须为true
(2)当obj1.hashCode() == obj2.hashCode()为false时,obj1.equals(obj2)必须为false
如果不重写equals,那么比较的将是对象的引用是否指向同一块内存地址,重写之后目的是为了比较两个对象的value值是否相等。特别指出利用equals比较八大包装对象
(如int,float等)和String类(因为该类已重写了equals和hashcode方法)对象时,默认比较的是值,在比较其它自定义对象时都是比较的引用地址
hashcode是用于散列数据的快速存取,如利用HashSet/HashMap/Hashtable类来存储数据时,都是根据存储对象的hashcode值来进行判断是否相同的。
这样如果我们对一个对象重写了euqals,意思是只要对象的成员变量值都相等那么euqals就等于true,但不重写hashcode,那么我们再new一个新的对象,
当原对象.equals(新对象)等于true时,两者的hashcode却是不一样的,由此将产生了理解的不一致,如在存储散列集合时(如Set类),将会存储了两个值一样的对象,
导致混淆,因此,就也需要重写hashcode()
举例说明:


        public override bool Equals(object obj) { if (!(obj is BalanceSheetReport expect)) return false; return this.EndSum == expect.EndSum && this.PreSum == expect.PreSum && this.SequenceId == expect.SequenceId && this.ProjectId == expect.ProjectId; } public override int GetHashCode() { return this.PreSum.GetHashCode() ^ this.EndSum.GetHashCode() ^ this.SequenceId.GetHashCode() ^ this.ProjectId.GetHashCode(); }
 
 

常见equals方法实现错误

1-未使用@override对覆盖超类的方法进行标记.

复制代码
public class Employee {
    public boolean equals(Employee other) {


    return Objects.equals(name, other.name) && salary ==other.salary && Objects.equals(hireDay, other.hireDay);
}
复制代码

这个方法声明的显示参数类型是Employee. 其结果并没有覆盖Object中的equals方法,而是定义了一个完全无关的方法.为了避免发生类型错误,可以使用@override对覆盖超类的方法进行标记.

2-没有同时override hashcode()函数 

object对象中的 public boolean equals(Object obj),对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象时,此方法才返回 true;
注意:当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。如下:
(1)当obj1.equals(obj2)为true时,obj1.hashCode() == obj2.hashCode()必须为true
(2)当obj1.hashCode() == obj2.hashCode()为false时,obj1.equals(obj2)必须为false
如果不重写equals,那么比较的将是对象的引用是否指向同一块内存地址,重写之后目的是为了比较两个对象的value值是否相等。特别指出利用equals比较八大包装对象
(如int,float等)和String类(因为该类已重写了equals和hashcode方法)对象时,默认比较的是值,在比较其它自定义对象时都是比较的引用地址
hashcode是用于散列数据的快速存取,如利用HashSet/HashMap/Hashtable类来存储数据时,都是根据存储对象的hashcode值来进行判断是否相同的。
这样如果我们对一个对象重写了euqals,意思是只要对象的成员变量值都相等那么euqals就等于true,但不重写hashcode,那么我们再new一个新的对象,
当原对象.equals(新对象)等于true时,两者的hashcode却是不一样的,由此将产生了理解的不一致,如在存储散列集合时(如Set类),将会存储了两个值一样的对象,
导致混淆,因此,就也需要重写hashcode()
举例说明:


        public override bool Equals(object obj) { if (!(obj is BalanceSheetReport expect)) return false; return this.EndSum == expect.EndSum && this.PreSum == expect.PreSum && this.SequenceId == expect.SequenceId && this.ProjectId == expect.ProjectId; } public override int GetHashCode() { return this.PreSum.GetHashCode() ^ this.EndSum.GetHashCode() ^ this.SequenceId.GetHashCode() ^ this.ProjectId.GetHashCode(); }

猜你喜欢

转载自www.cnblogs.com/frankcui/p/10810677.html