簡體   English   中英

您是否需要為記錄覆蓋 hashCode() 和 equals()?

[英]Do you need to override hashCode() and equals() for records?

假設以下示例:

public record SomeRecord(int foo, byte bar, long baz)
{ }

如果我要將所述 object 添加到HashMap ,是否需要覆蓋hashCodeequals

不,您不需要定義自己的hashCodeequals 如果您希望覆蓋默認實現,您可以這樣做。

有關詳細信息,請參閱規范的第 8.10.3 節https://docs.oracle.com/javase/specs/jls/se14/preview/specs/records-jls.html#jls-8.10

請特別注意,實現您自己的這些版本的警告:

從 java.lang.Record 繼承的所有成員。 除非在記錄正文中明確覆蓋,否則 R 已隱式聲明了覆蓋 java.lang.Record 中的 equals、hashCode 和 toString 方法的方法。

如果 java.lang.Record 中的任何這些方法在記錄主體中顯式聲明,則實現應滿足 java.lang.Record 中指定的預期語義。

特別是,自定義的equals實現必須滿足記錄副本必須等於記錄的預期語義。 這通常不適用於類(例如,如果兩個Car對象的VIN值相同,即使owner字段不同,它們也可能equals ),但對於記錄來說必須是正確的。 這個限制意味着很少有任何理由覆蓋equals

您是否需要它的答案實際上是 -這取決於您決定創建為Record的實體的實現。 在編譯或運行時也沒有任何限制來約束您這樣做,並且無論如何擴展Object的類總是如此。

另一方面,該提案的主要動機之一是“低價值、重復、容易出錯的代碼:構造函數、訪問器、 equals()hashCode()toString()”。 在數據載體中,這在當今的 Java 編程中經常暗示。 因此,進一步陳述的決定是更喜歡語義目標

...:將數據建模為數據。 (如果語義正確,樣板將自行處理。)聲明淺不可變、行為良好的名義數據聚合應該簡單、清晰和簡潔。

尾巴

因此,樣板文件已得到處理,但請注意,您可能仍出於某種原因希望您的記錄組件之一不被視為兩個不同對象之間比較過程的一部分,這就是您可能想要覆蓋的地方提供的equalshashCode的默認實現。 此外,毫無疑問,我的想法是有時需要toString的幻想,因此也需要覆蓋它。

以上大多不能歸類為編譯或運行時失敗,但提案本身讀取了它帶來的風險:

從 state 描述自動派生的任何成員也可以顯式聲明。 但是,粗心地實現訪問器或 equals/hashCode 可能會破壞記錄的語義不變量。

注意:后者主要是我的觀點,這樣消費者會希望有各種靈活性,以便他們可以使用最新的功能,但在某種程度上,現有的實現曾經可以工作。你看, 向后兼容性在更大程度上很重要,因為在升級期間很好。)

什么是 Java 記錄? 關於 Java 最常見的抱怨之一是您需要編寫大量代碼才能使 class 有用。 很多時候,您需要編寫以下內容:

  1. toString()
  2. 哈希碼()
  3. 等於()
  4. 吸氣劑方法
  5. 公共構造函數

對於簡單的領域類,這些方法通常很無聊、重復,並且可以很容易地機械生成(IDE 通常提供這種能力),但截至目前,語言本身並沒有提供任何方法來做到這一點.

記錄的目標是擴展 Java 語言語法並創建一種方式來表示 class 是“字段,只是字段,只有字段”。 通過您做出關於 class 的聲明,編譯器可以通過自動創建所有方法並使所有字段參與諸如 hashCode() 之類的方法來提供幫助。

記錄帶有用於記錄內所有屬性的hashCode()equals()toString()的默認實現

hashCode() 的默認實現

記錄將使用記錄內所有屬性的 hash 代碼

默認實現 equals()

記錄將使用所有屬性來決定兩個記錄是否相等

所以任何 hash 實現,例如 HashSet、LinkedHashSet、HashMap、LinkedHashMap、

etc 將使用hashCode()並且在任何沖突的情況下將使用equals()

默認實現還是自定義實現?

如果你想使用hashCode()equals()中的所有屬性,那么不要覆蓋

您是否需要為記錄覆蓋 hashCode() 和 equals()?

保留默認實現或 select 僅由您決定

任何東西,但是如果您想要自定義屬性,您可以覆蓋以決定哪些屬性決定相等性和屬性使 hashCode

默認實現中hashCode是如何計算的?

將使用 integer 的 hashCode 和這樣的字符串:

    int hashCode = 1 * 31;
    hashCode = (hashCode + "a".hashCode()) & 0x7fffffff;

, 下面的代碼默認實現hashCode() , equals()

toString()

HashSet 沒有添加它們兩個,因為這兩個記錄具有相同的 hashCode 和 equals

    static record Record(int id, String name) {

    }

    public static void main(String[] args) {
        Record r1 = new Record(1, "a");
        Record r2 = new Record(1, "a");

        Set<Record> set = new HashSet<>();
        set.add(r1);
        set.add(r2);
        System.out.println(set);

        System.out.println("Hashcode for record1: " + r1.hashCode());
        System.out.println("Hashcode for record2: " + r2.hashCode());

        int hashCode = 1 * 31;
        hashCode = (hashCode + "a".hashCode()) & 0x7fffffff;
        System.out.println("The hashCode: " + hashCode);
    }

, output

[Record[id=1, name=a]]
Hashcode for record1: 128
Hashcode for record2: 128
The hashCode: 128

, 資源:

博客 oracle

暫無
暫無

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

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