簡體   English   中英

理解有關Class的FindBugs警告不會覆蓋超類中的equals

[英]Understanding FindBugs warning about Class doesn't override equals in superclass

根據Josh Bloch在Java中的說法:

除非您願意放棄面向對象抽象的好處,否則無法擴展可實例化的類並在保留equals合同的同時添加值組件。

所以這就是我的情況,我有一個類Foo ,它已經覆蓋了Intellij Idea實現的equals()hashcode() 現在我有另一個類FooChild ,它擴展了Foo並為'Foo`增加了幾個字段。 現在,FindBugs抱怨FooChild:

類不會覆蓋超類中的equals此類擴展一個定義equals方法並添加字段的類,但不定義equals方法本身。 因此,此類的實例上的相等性將忽略子類的標識和添加的字段。 確保這是預期的,並且您不需要覆蓋equals方法。 即使您不需要重寫equals方法,也可以考慮覆蓋它,以便記錄子類的equals方法只返回調用super.equals(o)的結果這一事實。

我的問題是“ 這個類的實例上相等是什么意思會忽略子類的身份 ?我理解忽略添加字段的部分,因為還沒有為它們編寫equals()方法。

“類的實例上的相等性將忽略子類的身份是什么意思?”

如果類Foo有一個equals()方法,那么FooChild繼承它,這意味着如果使用equals()比較FooChild兩個實例,將調用Foo.equals()方法。

如果FooChild有任何數據成員,那么FooChild兩個實例可能在其Foo部分中具有相同的成員值,但是直接在類中定義的成員的值不同。 但是Foo.equals()方法只會查看Foo定義的成員,因此將兩個這樣的對象發音為equals() ,即使它們的FooChild部分不同。

這就是你需要在FooChild重寫equals()FooChild

現在,如果你使用equals()比較一個Foo和一個FooChild ,當兩個類都有自己的equals()版本時會發生什么? 它取決於你調用equals()對象,它取決於你如何實現兩個equals()方法。 坦白說,這太亂了! 這就是第一句話的意思,即Josh Bloch的意思。 定義這兩個equals()方法是不可能的,所以它們總能做正確的事情。 因此,最好避免一個值類(即,其標識與其成員變量的值綁定的類)擴展另一個值類的情況。

首先,在計算equalshashCode方法時,不應該包括來自超類的任何狀態。 該狀態超出了您正在處理的類的范圍,您的類應該依賴於超類而不是重寫其功能。

當使用IntelliJ生成equalshashCode時,如果要包含來自父類的任何計算,只需對添加對super.equals(o)super.hashCode()的調用的方法進行一些小修改。

實現這些方法的另一種方法是使用Apache Commons Lang庫中的EqualsBuilderHashCodeBuilder ,這些構建器提供語義來添加超級調用。

編輯:

回答“這個類的實例上的相等性是什么意思會忽略子類的身份?” :正如您已經指出的那樣,這意味着如果您有兩個類,一個擴展另一個類,並且子類不會覆蓋equalshashCode ,那么,在檢查兩個實例是否相等(或添加到散列結構)時兩者都表現得好像是來自同一個班級。 這是一個例子:

class A {
   private int intField = 2;

   public A(int value) {
       intField = value;
   }

   public boolean equals(Object o) {
       if (null == o) return false;
       if (this == o) return true;
       if (!(o instanceof A)) return false;

       return intField == ((A) o).intField;
   }

   public int hashCode() {
       return 11 * intField;
   }
}

class B extends A {
   private boolean boolField = true;

   public B(int intValue, boolean boolValue) {
       super(intValue);
       boolField = boolValue;
   }

   // no equals or hashCode
}

因此,對於這些課程,您將面臨以下問題:

A a = new A(12);
B b = new B(12, false);
b.equals(a);    // returns true

為了避免這種情況,只需在IntelliJ中生成它們之后在equalshashCode方法中添加一個超級調用。

class B extends A {
   private boolean boolField = true;

   public B(int intValue, boolean boolValue) {
       super(intValue);
       boolField = boolValue;
   }

   public boolean equals(Object o) {
       if (null == o) return false;
       if (this == o) return true;
       if (!(o instanceof B)) return false;
       if (!super.equals(o)) return false;

       return boolField == ((B) o).boolField;
   }

   public int hashCode() {
       int hash = super.hashCode();
       hash += 11 * Boolean.valueOf(boolField).hashCode();
       return hash;
   }

}

正如我已經說過的,其他方式是使用Apache Commons Lang庫中的構建器。

處理這類問題的通常方法是采用組合而不是繼承。 這是必要的,因為正如Josh Block在其出色的書“Effective Java”中詳述的那樣,你不能真正覆蓋equals(Object)的實現,它在不破壞equals(Object)的一般契約的情況下考慮屬性。

例:

class Foo {
    boolean property;

    public boolean equals(Object that) {
        return this == that
            || (that instanceof Foo)
                && this.property == ((Foo) that).property;
    }

    public int hashCode() { ... } // needs to be consistent with equals(Object).
}

class Bar extends Foo {
    boolean anotherProperty;

    // This is broken - DO NOT USE IT!
    public boolean equals(Object that) {
        return super.equals(that)
            && (that instanceof Bar)
            && this.anotherProperty == ((Bar) that).anotherProperty
    }

    public int hashCode() { ... } // needs to be consistent with equals(Object).
}

現在為什么Bar.equals(Object)的實現被破壞了? 僅僅因為它打破了對稱性! 這是證明:

Foo foo = new Foo();
Bar bar = new Bar();
assert foo.equals(bar); // passes because bar is a Foo and property is zero
assert bar.equals(foo); // throws up because foo isn't an instance of Bar!

使用繼承無法解決這類問題。 你需要使用構圖。

因此,在覆蓋它們時聲明equals(Object)和hashCode()方法通常是一個好主意,這樣它們就不會被破壞的實現再次覆蓋。

暫無
暫無

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

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