首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

深入 Java equals 契約:對稱性陷阱與最佳實踐

     編輯:Java 相關知識

equals 核心契約 (JLS)

  1. 自反性:o.equals(o) == true
  2. 對稱性:o.equals(o1) → o1.equals(o)
  3. 傳遞性:o.equals(o1) ∧ o.equals(o2) → o1.equals(o2)
  4. 一致性:物件未修改時多次調用結果一致
  5. 非空性:o.equals(null) == false

Object 預設實現為參考比較(==),子類若需值比較必須覆寫。

經典陷阱:instanceof 破壞對稱性

JDK FieldPosition.equals 使用 instanceof 檢查:

if (!(obj instanceof FieldPosition)) return false;

測試代碼:

FieldPosition fp = new FieldPosition(10);
FieldPosition fp1 = new MyTest(10); // 子類
fp.equals(fp1)   // true
fp1.equals(fp)   // false  ← 對稱性失效!

原因:子類 instanceof 父類為 true,父類 equals 通過;但子類 equals 要求 instanceof MyTest,父類實例失敗。

正確的型別檢查策略

  • final 類別:可安全使用 instanceof(無子類)。
  • 非 final 類別:必須使用 obj.getClass() == this.getClass() 或 getClass() == Xxx.class(若不希望子類複用 equals)。
  • 快速檢查:if (this == obj) return true;

完整實現範本

public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null || getClass() != obj.getClass()) return false;
    MyClass other = (MyClass) obj;
    return Objects.equals(field1, other.field1)
        && field2 == other.field2
        && Objects.equals(field3, other.field3);
}

父類實現可用反射遍歷 getDeclaredFields() 自動比較所有欄位,確保子類不覆寫也能正確工作。

equals 與 hashCode 不可分割

規約:a.equals(b) → a.hashCode() == b.hashCode()。

反例:PhoneNumber 覆寫 equals 但未覆寫 hashCode,導致 HashMap 以相等物件為鍵無法查找。

Map m = new HashMap();
PhoneNumber pn = new PhoneNumber(123, 38942, 230);
m.put(pn, "value");
m.get(new PhoneNumber(123, 38942, 230)); // 返回 null!

務必同步覆寫 hashCode,通常採用 Objects.hash(fields...) 或經典質數乘加算法。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved