读《Effective Java》笔记 - 条目10

发布于:2024-11-28 ⋅ 阅读:(15) ⋅ 点赞:(0)

条目10:在重写equals方法时要遵守通用约定

为什么需要重写 equals 方法?

  • 对象比较的需求: 默认情况下,Object 类的 equals 方法是通过对象引用进行比较的,即只有当两个对象是同一个实例时才返回 true。这对于逻辑上“值相等”的比较(value equality)是不够的。例如,String 类重写了 equals 方法,比较的是字符串内容而不是引用。
  • 需要自定义比较逻辑: 在某些情况下,类需要基于特定的属性定义两个对象的相等性。例如,在一个 Person 类中,可以通过 nameid 来判断两个 Person 对象是否相等。

通用约定

  1. 自反性(Reflexive)
    • 对于任何非空引用值 xx.equals(x) 必须返回 true
  2. 对称性(Symmetric)
    • 对于任何非空引用值 xy,如果 x.equals(y) 返回 true,那么 y.equals(x) 也必须返回 true
  3. 传递性(Transitive)
    • 对于任何非空引用值 xyz,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 必须返回 true
  4. 一致性(Consistent)
    • 对于任何非空引用值 xy,在对象未被修改的情况下,多次调用 x.equals(y) 必须始终返回相同的结果。
  5. 非空性(Non-nullity)
    • 对于任何非空引用值 xx.equals(null) 必须返回 false

正确重写equals 方法

  1. 使用 == 检查“自反性”

    if (this == obj) return true;
    
  2. 使用 instanceof 检查类型

    if (!(obj instanceof MyClass)) return false;
    
  3. 比较关键字段

    MyClass other = (MyClass) obj; // 类型转换
    return field1.equals(other.field1) && field2 == other.field2;
    
  4. 考虑性能和空值

    对可能为 null 的字段,使用 Objects.equals 或提前检查:

    Objects.equals(field1, other.field1)
    

完整代码

import java.util.Objects;

public class Person {
    private final String name;
    private final int id;

    public Person(String name, int id) {
        this.name = name;
        this.id = id;
    }

    @Override
    public boolean equals(Object obj) {
        // 自反性检查
        if (this == obj) return true;

        // 类型检查
        if (!(obj instanceof Person)) return false;

        // 强制转换后比较字段
        Person other = (Person) obj;
        return id == other.id && Objects.equals(name, other.name);
    }

    @Override
    public int hashCode() {
        // 始终与 equals 一致
        return Objects.hash(name, id);
    }
}

需要注意:

  1. 如果重写了 equals,也必须重写 hashCode,否则会违反 hashCode 的契约规则,导致在集合(如 HashMapHashSet)中行为异常。
  2. 不要在 equals 方法中比较不会影响逻辑相等性的字段,如缓存值或计算中间结果。
  3. 如果一个对象的字段参与了 equals 的比较逻辑,建议该字段是不可变的(final),以避免修改后违反一致性。
  4. 如果类实现了 Comparable 接口,那么 equals 的定义应该与 compareTo 保持一致,即 x.compareTo(y) == 0 时,x.equals(y) 必须返回 true

违法约定的风险

  1. 在集合中的行为异常。 如果 equalshashCode 不一致,可能导致 HashMapHashSet 无法正确存取数据。
  2. 不符合用户预期。如果 equals 实现不符合通用约定,可能在业务逻辑中引入难以发现的错误。

自动工具

  • 可以使用一些注解自动生成equals等方法。如lombok
  • 使用IDEA快速生成

网站公告

今日签到

点亮在社区的每一天
去签到