热爱技术,追求卓越
不断求索,精益求精

秒懂java,你真的会写equals方法吗?

java中,Object类中有一个equals方法:

public boolean equals(Object obj) {
    return (this == obj);
}

默认实现,就是两个对象完全相等才返回true,即判断的是引用的地址是否相同。

大多数情况下,我们都不需要覆盖Object的equals方法。但是有时候我们需要从逻辑上判断两个事物是否等同,比如张三的通讯录里存了一个姓名为“王二”的电话号码,李四的通讯录里也存了一个姓名为“王二”的电话号码,两个通讯录里的这两条记录是否相同呢?我们除了比较“王二”这个姓名外,还要比较电话号码是否相同,如果电话号码也相同则就是同一个人的联系方式。虽然这两条记录并没有在同一个通讯录中,就是说没有相同的地址引用,但我们认为这两套记录是等同的。

所以我们通常在类具备特有的“逻辑相等”概念的时候,会重写Object的equals方法,但是重写equals方法有很多我们需要注意的细节。

覆盖equals方法必须遵循的约定

覆盖equals方法必须遵循自反性(reflexive)、对称性(symmetric)、传递性(transitive)、一致性(consistent)、非null对象与null进行equals比较必为false等约定。

  • 自反性:a!=null,则a.equals(a)返回true.
  • 对称性:a!=null,b!=null,则a.equals(b) == b.equals(a).
  • 传递性:a!=null,b!=null,c!=null,如果a.equals(b) == b.equals(c),这a.equals(c).
  • 一致性:a!=null,b!=null,如果a,b没有被修改,那么不管调用多少次equals,返回的结果始终只有一个.
  • 非null对象与null进行equals比较必为false.

正确的写equals方法的步骤

  1. 使用“==”操作符检查参数是否是这个对象的引用。
  2. 检查参数是否为空。
  3. 检查参数类型是否匹配。
  4. 把参数转换为正确的类型。
  5. 对于类中需要进行逻辑判断的属性,检查参数的属性是否与这个对象的属性相同(比较属性也使用对于属性所属类型的equals方法)。
  6. 编写完成后,验证是否满足对称、传递、一致等特性。

写equals方法需要注意的地方

  1. 覆盖equals时总要覆盖hashCode方法,避免一些预想不到的错误。
  2. 不要将equals声明中的Object对象替换为其他类型,因为参数不同的相同方法名是重载而不是重写,这可能让程序运行结果变得难以预料。

用一个实例结束

判断一个用户是否相同,除了用户名还有手机号必须相同,才能认为是同一个用户,重写equals和hashCode并做了简单的自反、对称、传递、一致、null比较等测试。

package cn.lovecto.test;

public class User {
    /** 用户名 */
    private String name;
    /** 手机号 */
    private String phone;
    public User(String name, String phone) {
        super();
        this.name = name;
        this.phone = phone;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone;
    }
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        result = prime * result + ((phone == null) ? 0 : phone.hashCode());
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        //1.使用“==”操作符检查参数是否是这个对象的引用
        if (this == obj){
            return true;
        }
        //2.检查参数是否为空
        if (obj == null){
            return false;
        }
        //3.检查参数类型是否匹配
        if (getClass() != obj.getClass())
            return false;
        //4.把参数转换为正确的类型
        User other = (User) obj;
        //5.对于类中需要进行逻辑判断的属性,检查参数的属性是否与这个对象的属性相同
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        if (phone == null) {
            if (other.phone != null)
                return false;
        } else if (!phone.equals(other.phone))
            return false;
        return true;
    }
    public static void main(String[] args) {
        System.out.println("test begin");
        User a = new User("a", "135xxxxxxxx");
        User b = new User("a", "135xxxxxxxx");
        User c = new User("a", "135xxxxxxxx");
        //自反性
        assert a.equals(a);
        //传递性
        assert a.equals(b) == b.equals(c) ? a.equals(c) : false;
        //对称性
        boolean result = a.equals(b);
        assert result == b.equals(a);
        //一致性 
        for(int i = 0; i < 100; i++){
            assert result == a.equals(b);
        }
        //非null对象与null进行equals比较必为false
        assert a.equals(null) == false;
        System.out.println("test end");
        assert false;
    }
}

备注:要使用上面代码中的assert,Jvm参数中加入“-ea”选项。执行结果如下:

test begin
Exception in thread "main" test end
java.lang.AssertionError
    at cn.lovecto.test.User.main(User.java:86)
赞(1)
未经允许不得转载:LoveCTO » 秒懂java,你真的会写equals方法吗?

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

热爱技术 追求卓越 精益求精