Object.equals() 必須滿足:
x.equals(x) == truex.equals(y) == true ⇒ y.equals(x) == truex.equals(y) && y.equals(z) ⇒ x.equals(z)x.equals(null) == false此外,equals 與 hashCode 必須一致:相等對象哈希碼必須相等。
public boolean equals(Object obj) {
if (obj == null) return false;
if (!(obj instanceof FieldPosition)) return false;
FieldPosition other = (FieldPosition) obj;
if (attribute == null) {
if (other.attribute != null) return false;
} else if (!attribute.equals(other.attribute)) return false;
return (beginIndex == other.beginIndex
&& endIndex == other.endIndex
&& field == other.field);
}
看似正確的 instanceof 檢查在繼承場景下會失效:
class MyTest extends FieldPosition {
int x = 10;
public MyTest(int x) { super(x); this.x = x; }
public boolean equals(Object o) {
if (o == null) return false;
if (!(o instanceof MyTest)) return false; // 只比較同類
return ((MyTest)o).x == this.x;
}
}
// 測試
FieldPosition fp = new FieldPosition(10);
FieldPosition fp1 = new MyTest(10);
System.out.println(fp.equals(fp1)); // true (父類 instanceof 子類 為 true)
System.out.println(fp1.equals(fp)); // false (子類 instanceof 父類 為 true,但父類非 MyTest)
結果:對稱性被破壞!
instanceof 語義:"子類是父類的實例" ⇒ 子類 instanceof 父類返回 true。equals 用 instanceof FieldPosition 檢查,子類實例通過檢查,進入字段比較(可能因缺少子類字段而返回 true)。equals 用 instanceof MyTest 檢查,父類實例失敗,直接返回 false。類聲明 final 時無繼承風險,可放心用 instanceof 優化。
public boolean equals(Object obj) {
if (this == obj) return true; // 同一引用快速返回
if (obj == null) return false;
if (getClass() != obj.getClass()) return false; // 精確類型匹配
MyClass other = (MyClass) obj;
// 比較所有關鍵字段...
return field1 == other.field1 && Objects.equals(field2, other.field2);
}
關鍵點:使用 getClass() != obj.getClass() 而非 obj.getClass() != MyClass.class,確保子類未覆寫 equals 時也能正確工作(子類實例 getClass() 返回子類 Class,不等於父類 Class,返回 false,避免錯誤比較)。
若設計允許子類比較(如值對象),需在父類文檔說明,子類必須覆寫 equals 並調用 super.equals(),但極易出錯,一般不推薦。
getClass() 精確類型檢查,除非類是 final。==,引用類型用 Objects.equals() (Null 安全)。@EqualsAndHashCode 減少手寫錯誤。