首页 > 学院 > 开发设计 > 正文

第八条 覆盖equals请遵守通用的约定(一)

2019-11-06 07:38:29
字体:
来源:转载
供稿:网友
第八条 覆盖equals请遵守通用的约定(一)对象的equsls()方法经常会用到,没用过的都不好意思说自己是做java的。那么,这个方法怎么用,或者有什么用处,下面慢慢讲解。类被实例化出来对象,对象本质上都是唯一的,如果不重写equals()方法,那么,每个类都只与它自身相等。以下四种情况适用这个形式:一、类的每个实例本质上是唯一的,一般可用 == 来比较,此时比较的是在内存中的地址值,如果两个对象指向一个地址值,那么就认为这两个实例对象是同一个。二、不关心是否提供了逻辑相等的功能。比如一些工具类,例如随机数的Random,我们使用时是要返回一个随机数,基本用不到去比较两个Random是否是同一个对象。三、父类已经覆盖了equals()方法,子类不用重写就够用了,例如 List实现从AbstractList类继承了equals,使用equals()方法时,直接调用父类的就够用了。四、如果类或者包是私有的,那么它的equals方法不会被调用,此时,为了防止子类错误的使用equals方法,或者对象调用此方法,应该覆盖此方法,在方法内部抛出一个异常,加以说明。以上是创建对象或使用对象,不考虑equals()方法的场景,下面说说使用的原则和什么时候使用。equals()方法使用时有几个原则,自反性、对称性、传递性、一致性、非空性。对任何一个非null的对象x和y及z,x.equals(x)必须返回true,x.equals(y) 和 y.equals(x) 的值必须相等,x.equals(null)必须返回false;如果x.equals(y)为true,y.equals(z)为true,那么x.equals(z)也必须为true。这么说可能有点笼统,那么简单的举个例子。public class Student {    PRivate String name;    private int age;    public Student(String name, int age) {        this.name = name;        this.age = age;    }    public Student() {    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }}班级里面有几十个学生,比较这些学生的信息,如果名字和年龄一致,就说明是同一个人,如果不一致,就是不同的人。(假如班级没有重名的)此时,Student s1 = new Student("jim",12);Student s2 = new Student("jim",12);System.out.println(s1 == s2);System.out.println(s1.equals(s2));返回两个false,因为 == 比较的是地址值,只要不是单利模式或枚举类型,每一个new出来的都是唯一的,地址值是两个不同的值,所以为false。equals()方法调用父类,也就是Object的方法public boolean equals(Object o) {        return this == o;    }通过代码看出,比较的也是地址值,所以返回false。那么问题来了,怎么实现上述需求?此时,就要重写此方法了。重写的方法中按照自己的逻辑判断就可以了。只需要年龄和名字一致就行。@Override    public boolean equals(Object o) {        if(this == o){            return true;        }        if(o instanceof Student){            Student s = (Student) o;            return age == s.age && name.equals(s.name);        }                return false;    }此时,再执行上述两个方法,返回值为 false ,true。如果说学生信息又加了一个地址address,要这三项都一致才行,那么equals方法稍加变动 @Override    public boolean equals(Object o) {        if(this == o){            return true;        }        if(o instanceof Student){            Student s = (Student) o;            return age == s.age && name.equals(s.name) && address.equals(s.address);        }        return false;    }如果说需求变了,只要名字一致就是一个人,不需要考虑年龄,那么equals方法可以写成下面这样 @Override    public boolean equals(Object o) {        if(this == o){            return true;        }        if(o instanceof Student){            Student s = (Student) o;            return  name.equals(s.name);        }        return false;    }说道这,大家应该明白,可以控制equals方法返回一些信息,明明是不同的地址值,但只要信息内容一致或部分一致,我们都可以当成是一个人来处理。以上是最基本的用法,下面要重点说数对称性和传递性。所谓对称,就是s1.equals(s2) == s2.equals(s1),上述代码中肯定可以,但一些特殊的对象写法,一不留神,就会出问题。比如effective中举的例子,用复合的方式写了一个不区分大小写的String类,public class CaseString {    private final String s;    public CaseString(String s) {        if(s == null){            throw new NullPointerException();        }        this.s = s;    }    @Override    public boolean equals(Object o) {        if(o instanceof CaseString){            return s.equalsIgnoreCase(((CaseString) o).s);        }        if(o instanceof String){            return s.equalsIgnoreCase((String) o);        }        return false;    }    }这个意图很好,想把CaseString 类和 String 类也可以比较,如果CaseString s1 = new CaseString("abc");String s2 = "Abc";s1.equals(s2)返回true,但s2.equals(s1)确实false,因为String类中的equals方法却不知道区分大小写的,此类违反了对称性,因此,既然String类的equals不支持大小写相同,那么对CaseString类而言,只要不是同类型的都是false就可以了,同类型的话在调用equalsIgnoreCase方法,@Override    public boolean equals(Object o) {                return o instanceof CaseString && s.equalsIgnoreCase(((CaseString) o).s);           }另外一个容易出错的地方是传递性,相同类型的传递性出错基本不可能,出错的一般都是写子类扩展属性的情况。下一章分析。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表