簡體   English   中英

equals和hashCode的可重用實現

[英]Reusable implementation of equals and hashCode

我正在一個項目中,其中許多類需要equalshashCode適當典型實現:每個類具有一組最終字段,這些字段在構造時初始化為“深度”不可變的對象(在某些情況下,應接受null )用於哈希和比較。

為了減少樣板代碼的數量,我考慮過編寫一個提供此類行為常見實現的抽象類。

public abstract class AbstractHashable {

    /** List of fields used for comparison. */
    private final Object[] fields;

    /** Precomputed hash. */
    private final int hash;

    /**
     * Constructor to be invoked by subclasses.
     * @param fields list of fields used for comparison between objects of this
     * class, they must be in constant number for each class
     */
    protected AbstractHashable(Object... fields) {
        this.fields = fields;
        hash = 31 * getClass().hashCode() + Objects.hash(fields);
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (obj == null || !getClass().equals(obj.getClass())) {
            return false;
        }
        AbstractHashable other = (AbstractHashable) obj;
        if (fields.length != other.fields.length) {
            throw new UnsupportedOperationException(
                    "objects of same class must have the same number of fields");
        }
        for (int i=0; i<fields.length; i++) {
            if (!fields[i].equals(other.fields[i])) {
                return false;
            }
        }
        return true;
    }

    @Override
    public int hashCode() {
        return hash;
    }

}

打算這樣使用:

public class SomeObject extends AbstractHashable {

    // both Foo and Bar have no mutable state
    private final Foo foo;
    private final Bar bar;

    public SomeObject(Foo foo, Bar bar) {
        super(foo, bar);
        this.foo = Objects.requireNonNull(foo);
        this.bar = bar; // null supported
    }

    // other methods, no equals or hashCode needed

}

這基本上是這里提出的內容,但有一些差異。

在我看來,這是一種減少冗長但仍有效的equalshashCode實現的簡單而有效的方法。 但是,由於我不記得曾經見過類似的東西(上面鏈接中的答案除外),所以我想特別問一下在應用之前是否有反對這種方法的觀點(或可能會有所改進)它貫穿整個項目。

我已經看到此方法有兩個問題:

  1. 當且僅當它們的所有字段都匹配時,兩個對象才被視為相等。 在現實世界中,情況並非總是如此。
  2. 通過使您的類從AbstractHashable extend ,您不再extend從任何其他類extend 重用equalshashCode付出了很高的代價。

通過將更多的元數據傳遞給AbstractHashable類可以解決第一個問題,該類允許它標識哪些字段是可選的。 例如,您可以將另一個數組傳遞給AbstractHashTable ,該數組包含通過setter作為其元素被忽略的索引位置。 第二個問題可以通過使用Composition解決。 對其進行重構,而不是擴展AbstractHashTable ,以便它可以與其用戶建立HAS-A關系,而不是IS-A關系。

但是,由於我不記得曾經見過類似的東西(上面鏈接中的答案除外),所以我想特別問一下這種方法是否有針對性

這種方法肯定會影響代碼的可讀性。 如果您想出一種更具可讀性的方法(例如通過使用批注),那么我想重用equalshashCode實現不會有任何問題。

綜上所述,諸如eclipse之類的現代IDE可以輕松為您生成equalshashCode實現,因此,真的有必要提出這種解決方案嗎? 我相信不會

根據我的經驗,這似乎很糟糕,因為您會增加在運行時與編譯時可能遇到的錯誤(記住,在列表等中對這些對象的所有使用。現在可以給您帶來擴展的行為)。 如果類中字段的順序不同怎么辦? 其次,您濫用繼承嗎? Maybee選擇某種框架( https://projectlombok.org/ ),該框架會基於注釋生成哈希碼和等號?

該解決方案應該起作用。

但是,從OOP的角度來看,它有點弱:

  • 您正在復制數據(超類中的字段與對象中的字段),因此您的對象大一倍; 另外,如果您需要使它們可變,則需要在兩個地方維護狀態
  • 類層次結構被強制,僅提供equals / hashCode

現代IDE很容易生成此類方法,如果您願意,還可以使用框架(如Lombok)或庫(如Guava的Objects / Java 8的Objects)來簡化這些方法。

我建議看看apache的HashCodeBuilderEqualsBuilder類。 它也具有基於反射的方法,例如reflectionHashCode和reflectionEquals。

暫無
暫無

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

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