簡體   English   中英

了解equals方法

[英]Understanding equals method

J. Bloch在他的有效Java中為equals方法的實現提供了幾條規則。 他們來了:

•Reflexive:對於任何非空引用值x,x.equals(x)必須返回true。

•對稱:對於任何非空引用值x和y,當且僅當y.equals(x)返回true時,x.equals(y)必須返回true。

•Transitive:對於任何非空引用值x,y,z,如果x.equals(y)返回true而y.equals(z)返回true,則x.equals(z)必須返回true。

•一致:對於任何非空引用值x和y,x.equals(y)的多次調用始終返回true或始終返回false,前提是不修改在對象上的equals比較中使用的信息。

•對於任何非空引用值x,x.equals(null)必須返回false。

但后來他在書中提到了所謂的利斯科夫替代原則:

Liskov替換原則表明類型的任何重要屬性也應該適用於其子類型,因此為該類型編寫的任何方法都應該在其子類型上同樣有效

我不知道它與equals合同的關系。 我們是否應該在編寫equals實現時堅持它?

問題是關於實現子類的方法。 以下是本書中的示例:

private static final Set<Point> unitCircle;

static {
    unitCircle = new HashSet<Point>();
    unitCircle.add(new Point(1, 0));
    unitCircle.add(new Point(0, 1));
    unitCircle.add(new Point(-1, 0));
    unitCircle.add(new Point(0, -1));
}

public static boolean onUnitCircle(Point p) {
    return unitCircle.contains(p);
}

public class CounterPoint extends Point {
    private static final AtomicInteger counter = new AtomicInteger();

    public CounterPoint(int x, int y) {
        super(x, y);
        counter.incrementAndGet();
    }

    public int numberCreated() { return counter.get(); }
}

以及以下實施:

// Broken - violates Liskov substitution principle (page 40)
@Override public boolean equals(Object o) {
    if (o == null || o.getClass() != getClass())
        return false;
    Point p = (Point) o;
    return p.x == x && p.y == y;
}

好的,違反了什么呢? 我不明白。

通常有兩種方法可以檢查equals方法中的類型:

選項1:instanceof

if (! (obj instanceof ThisClass)){
    return false;
}

該選項尊重 Liskov替代原則 但是你不能在子類這是相關的而不會斷裂的等價關系(自反,對稱,傳遞)的特性equals方法添加附加屬性。

選項2:getClass()

if (obj == null || ! this.getClass().equals(obj.getClass())) {
    return false;
}

該選項違反Liskov替換原則 但是,您可以在子類中添加與equals方法相關的其他屬性,而不會破壞等價關系的特征(自反,對稱,傳遞)。

Joshua Bloch在他的“Effective Java”一書中警告過這一點。

然而,Angelika Langer提到了一種“混合tpye”比較的方法,如果你可以為其他屬性定義默認值:

http://www.angelikalanger.com/Articles/JavaSolutions/SecretsOfEquals/Equals-2.html

缺點是等於方法變得相當復雜。

 // Broken - violates Liskov substitution principle (page 40) @Override public boolean equals(Object o) { if (o == null || o.getClass() != getClass()) return false; Point p = (Point) o; return px == x && py == y; } 

好的,違反了什么呢? 我不明白。

因此,如果您有一個子類,如MyPoint(可能會添加其他方法但不添加其他屬性/字段),那么

Point p1 = new Point(x, y);
Point p2 = new MyPoint(x, y);

p1.equals(p2) == false

Set<Point> points = new HashSet<>();
points.add(p1);

points.contains(p2) == false;

雖然兩個對象確實代表了同一點。

如果您改為使用選項1(instanceof),則equals方法將返回true。

我認為他試圖說一個點的特征是它的坐標。 所以你會期望這是真的:

new Point(0, 0).equals(new CounterPoint(0, 0));

因為這兩個點具有相同的坐標,即使它們沒有相同的類型。 但是建議的equals方法將返回false,因為這兩個對象具有不同的類。

如果您考慮集合,例如,這是真的:

new LinkedList().equals(new ArrayList());

這兩個列表的類型不同,但它們具有相同的內容(在這種情況下它們都是空的),因此被認為是相同的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM