重写equal()时为什么也得重写hashCode()之深度解读equal方法与hashCode方法渊源(三)

文章来源:

https://blog.csdn.net/javazejian/article/details/51348320

继续:

https://blog.csdn.net/u013412772/article/details/80376902

6.重写equals()中getClass与instanceof的区别

虽然前面我们都在使用instanceof(当然前面我们是根据需求(批次相同即相等)而使用instanceof的),但是在重写equals() 方法时,一般都是推荐使用 getClass 来进行类型判断(除非所有的子类有统一的语义才使用instanceof),不是使用 instanceof。我们都知道 instanceof 的作用是判断其左边对象是否为其右边类的实例,返回 boolean 类型的数据。可以用来判断继承中的子类的实例是否为父类的实现。下来我们来看一个例子:父类Person

public class Person {  
    protected String name;  
    public String getName() {  
        return name;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
    public Person(String name){  
        this.name = name;  
    }  
    public boolean equals(Object object){  
        if(object instanceof Person){  
            Person p = (Person) object;  
            if(p.getName() == null || name == null){  
                return false;  
            }  
            else{  
                return name.equalsIgnoreCase(p.getName ());  
            }  
        }  
        return false;  
   }  
}  

子类 Employee

public class Employee extends Person{  
    private int id;  
    public int getId() {  
        return id;  
    }  
    public void setId(int id) {  
        this.id = id;  
    }  
    public Employee(String name,int id){  
        super(name);  
        this.id = id;  
    }  
    /** 
     * 重写equals()方法 
     */  
    public boolean equals(Object object){  
        if(object instanceof Employee){  
            Employee e = (Employee) object;  
            return super.equals(object) && e.getId() == id;  
        }  
        return false;  
    }  
}  

上面父类 Person 和子类 Employee 都重写了 equals(),不过 Employee 比父类多了一个id属性,而且这里我们并没有统一语义。测试代码如下:

public class Test {  
    public static void main(String[] args) {  
        Employee e1 = new Employee("chenssy", 23);  
        Employee e2 = new Employee("chenssy", 24);  
        Person p1 = new Person("chenssy");  
        System.out.println(p1.equals(e1));  
        System.out.println(p1.equals(e2));  
        System.out.println(e1.equals(e2));  
    }  
}  

上面代码我们定义了两个员工和一个普通人,虽然他们同名,但是他们肯定不是同一人,所以按理来说结果应该全部是 false,但是事与愿违,结果是:true、true、false。对于那 e1!=e2 我们非常容易理解,因为他们不仅需要比较 name,还需要比较 ID。但是 p1 即等于 e1 也等于 e2,这是非常奇怪的,因为 e1、e2 明明是两个不同的类,但为什么会出现这个情况?首先 p1.equals(e1),是调用 p1 的 equals 方法,该方法使用 instanceof 关键字来检查 e1 是否为 Person 类,这里我们再看看 instanceof:判断其左边对象是否为其右边类的实例,也可以用来判断继承中的子类的实例是否为父类的实现。他们两者存在继承关系,肯定会返回 true 了,而两者 name 又相同,所以结果肯定是 true。所以出现上面的情况就是使用了关键字 instanceof,这是非常容易导致我们“钻牛角尖”。故在覆写 equals 时推荐使用 getClass 进行类型判断。而不是使用 instanceof(除非子类拥有统一的语义)。

7.编写一个完美equals()的几点建议

下面给出编写一个完美的equals方法的建议(出自Java核心技术 第一卷:基础知识):

1)显式参数命名为otherObject,稍后需要将它转换成另一个叫做other的变量(参数名命名,强制转换请参考建议5)

2)检测this与otherObject是否引用同一个对象 :if(this == otherObject) return true;(存储地址相同,肯定是同个对象,直接返回true)

3) 检测otherObject是否为null ,如果为null,返回false.if(otherObject == null) return false;

4) 比较this与otherObject是否属于同一个类 (视需求而选择)

  • 如果equals的语义在每个子类中有所改变,就使用getClass检测:if(getClass()!=otherObject.getClass()) return false; (参考前面分析的第6点)
  • 如果所有的子类都拥有统一的语义,就使用instanceof检测 :if(!(otherObject instanceof
    ClassName)) return false;(即前面我们所分析的父类car与子类bigCar混合比,我们统一了批次相同即相等)

5) 将otherObject转换为相应的类类型变量:ClassName other = (ClassName) otherObject;

6) 现在开始对所有需要比较的域进行比较 。使用==比较基本类型域,使用equals比较对象域。如果所有的域都匹配,就返回true,否则就返回flase。

  • 如果在子类中重新定义equals,就要在其中包含调用super.equals(other)
  • 当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明
    相等对象必须具有相等的哈希码 。

猜你喜欢

转载自blog.csdn.net/u013412772/article/details/80377037