x.equals(x) == truex.equals(y) == y.equals(x)x.equals(y) && y.equals(z) → x.equals(z)x.equals(null) == falsejava.text.FieldPosition.equals() 使用 instanceof 檢查:
public boolean equals(Object obj) {
if (obj == null) return false;
if (!(obj instanceof FieldPosition)) return false; // 違反對稱性隱患
FieldPosition other = (FieldPosition) obj;
// 比較字段...
}
import java.text.FieldPosition;
public class EqualsSymmetryTest {
public static void main(String[] args) {
FieldPosition fp = new FieldPosition(10);
FieldPosition fpSub = new MyFieldPosition(10); // 子類
System.out.println("fp.equals(fpSub): " + fp.equals(fpSub)); // true
System.out.println("fpSub.equals(fp): " + fpSub.equals(fp)); // false!
}
}
class MyFieldPosition extends FieldPosition {
int x = 10;
public MyFieldPosition(int x) { super(x); this.x = x; }
@Override
public boolean equals(Object o) {
if (o == null) return false;
if (!(o instanceof MyFieldPosition)) return false; // 更嚴格檢查
return ((MyFieldPosition)o).x == this.x;
}
}
輸出:
fp.equals(fpSub): true fpSub.equals(fp): false
原因:父類 instanceof FieldPosition 匹配子類,子類 instanceof MyFieldPosition 不匹配父類。
public final class Point {
private final int x, y;
public Point(int x, int y) { this.x = x; this.y = y; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; // 精確類匹配
Point point = (Point) o;
return x == point.x && y == point.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
}
public class Shape {
private final String color;
public Shape(String color) { this.color = color; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; // 關鍵:精確類匹配
Shape shape = (Shape) o;
return Objects.equals(color, shape.color);
}
@Override
public int hashCode() {
return Objects.hash(color);
}
}
// 子類必須覆寫 equals/hashCode 並包含父類字段
public class Circle extends Shape {
private final double radius;
public Circle(String color, double radius) {
super(color);
this.radius = radius;
}
@Override
public boolean equals(Object o) {
if (!super.equals(o)) return false; // 先檢查父類
if (getClass() != o.getClass()) return false;
Circle circle = (Circle) o;
return Double.compare(radius, circle.radius) == 0;
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), radius);
}
}
public abstract class Entity {
private final Long id;
@Override
public boolean equals(Object o) {
return o instanceof Entity other
&& this.getClass() == other.getClass() // 精確類型
&& Objects.equals(this.id, other.id);
}
@Override
public int hashCode() {
return Objects.hashCode(id);
}
}
| 錯誤 | 後果 | 修正 |
|---|---|---|
| 用 instanceof 檢查非 final 類 | 違反對稱性 | 用 getClass() 或 final 類 |
| 只比較部分字段 | 違反傳遞性/一致性 | 比較所有標識字段 |
| equals 用 getClass,hashCode 用 instanceof | 違反 hashCode 契約 | 兩者邏輯一致 |
| 可變字段參與 equals | 放入 Set/Map 後修改導致找不到 | 只用不可變字段 |
| 忘記檢查 null | NPE | 首行 if (o == null) return false |
getClass(),適用於 final 類或不打算被繼承的類final + getClass() 檢查hashCode()Objects.equals() 處理 null 安全比較==,引用類型用 Objects.equals()Double.compare()/Float.compare()Arrays.equals()/Arrays.deepEquals()正確的 equals 實現是集合框架正常工作的前提,違反契約會導致 HashSet、HashMap、ArrayList.contains() 等出現難以排查的邏輯錯誤。