Effective Java -- 重写equals方法的通用约定

Posted 十木禾

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Effective Java -- 重写equals方法的通用约定相关的知识,希望对你有一定的参考价值。

这篇博客承接上一篇博客,我们来讲讲重写equals() 方法时候要满足的性质——传递性

用通俗的话来解释传递性就是说:如果A等于B,然后B等于C,那么我们就可以说A等于C


以下我们来举出一个反面例子来帮助理解一下传递性的体现

首先我们有一个Point 类该类有横纵坐标的属性(xy),并且重写了equals() 方法

package com.blog.effective.note8;

/**
 * 〈一个点类〉<br>
 *
 * @author 未绪
 * @time 2017/12/17 11:50
 */
public class Point 

    //该点是二位平面上的点
    private int x;
    private int y;

    public Point(int x,int y)
        this.x=x;
        this.y=y;
    

    @Override
    public boolean equals(Object obj) 
        if(obj instanceof Point)
            Point p = (Point)obj;
            return p.x==this.x&&p.y==this.y;
        
        return false;
    

以上的代码可以知道,如果有两个Point 对象,那么,只要对应的横纵坐标的数值相等,那么就可以判断这两个点是同一个点。

    public static void main(String[] args) 

        Point p1=new Point(10,20);
        Point p2=new Point(10,20);

        System.out.println(p1.equals(p2));      // 输出 true
    

然后这个时候我们需求增加了,我们要给这个点加上一个Color 属性,来说明这个点是什么颜色的。

如果我们不重写相应的equals() 方法,显然在比较相等的时候会忽略掉颜色的信息。

如下代码

package com.blog.effective.note8;

import java.awt.*;

/**
 * 〈一个带有颜色的点〉<br>
 *
 * @author 未绪
 * @time 2017/12/17 11:52
 */
public class ColorPoint extends Point 

    //给改点新增加一个颜色元素
    private Color color;

    public ColorPoint(int x, int y, Color color) 
        super(x, y);
        this.color = color;
    

    @Override
    public boolean equals(Object obj) 
        if (obj instanceof ColorPoint) 
            ColorPoint cp = (ColorPoint) obj;
            return super.equals(obj) && this.color == cp.color;  //
        
        return false;

    

这个时候我们来测试一下我们重写的equals() 方法

        Point point=new Point(1,1);
        ColorPoint colorPoint=new ColorPoint(1,1,Color.BLUE);

        System.out.println(point.equals(colorPoint));       //true
        System.out.println(colorPoint.equals(point));       //false

我们看到输出得结果就知道这种做法不满足对称性。
point.equals(colorPoint) 调用的是Point 类的equals() 方法,由于忽略了颜色信息,所以只要坐标相等就会返回 true
colorPoint.equals(point) 调用的是ColorPoint 类的equals() 方法,point点无颜色信息,所以就会返回false

我们可以尝试在ColorPoint 类的equals() 方法在进行混合比较的时候忽略颜色的信息

    @Override
    public boolean equals(Object obj) 

        if(obj instanceof ColorPoint)
            // 非混合比较
            ColorPoint cp = (ColorPoint) obj;
            return super.equals(obj) && this.color == cp.color;
        

        if (obj instanceof Point) 
            //混合比较
            return obj.equals(this);
        

        return false;
    

这样的做法满足了对称性,但是牺牲了传递性,如下测试代码

        ColorPoint cp1=new ColorPoint(1,1, Color.BLUE);
        Point p=new Point(1,1);
        ColorPoint cp2=new ColorPoint(1,1, Color.RED);

        System.out.println(cp1.equals(p));      //true
        System.out.println(p.equals(cp2));      //true
        System.out.println(cp1.equals(cp2));    //false

cp1.equals(p)p.equals(cp2) 的的比较都是忽略颜色信息的,但是cp1.equals(cp2) 之间的比较却是要考虑颜色信息。


考虑如何解决这样的一个问题呢?

equals() 方法中使用getClass() 方法代替instanceof ,将Point 类中的equals() 方法写为

    @Override
    public boolean equals(Object obj) 
        if (obj == null || this.getClass() != obj.getClass()) 
            return false;
        
        Point p = (Point) obj;
        return p.x == this.x && p.y == this.y;
    

这样子的话,就只又对象具有相同的实现的时候,才能使对象相等。但是这样的结果应该是无法接受的。


还有一种不错的建议就是——使用复合

我们不再使ColorPoint 继承Point ,而是在其中加入一个Point 属性。

public class ColorPoint 

    //给改点新增加一个颜色元素
    private Color color;
    private Point point;

    public ColorPoint(int x, int y, Color color) 
        point=new Point(x,y);
        this.color = color;
    
    @Override
    public boolean equals(Object obj) 
        if (obj instanceof ColorPoint) 
            ColorPoint cp = (ColorPoint) obj;
            return cp.point.equals(obj) && this.color == cp.color;
        
        return false;
    

以上是关于Effective Java -- 重写equals方法的通用约定的主要内容,如果未能解决你的问题,请参考以下文章

Effective Java 第三版——10. 重写equals方法时遵守通用约定

Effective Java总结的78条

Effective java -- 2 对于所有对象都通用到方法

Java:Effective java学习笔记之 覆盖equals时请遵守通用约定

Java:Effective java学习笔记之 覆盖equals时总要覆盖hashcode

[读书笔记]Effective Java 第三章