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;
    }
}
發佈了179 篇原創文章 · 獲贊 230 · 訪問量 56萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章