Effective Java之覆盖equal时要遵守通用约定(八)

来源:互联网 发布:上海地铁线路图软件 编辑:程序博客网 时间:2024/05/16 19:40

先介绍一下Object的equal作用,==代表物理上的相等,equal代表逻辑上的相等,Object的equal的方法其实等同于==,这是因为它的逻辑是“如果对象物理相等,那么它们就逻辑相等”。
 

1.何时不需要覆盖

1.如果每个实例的本质是唯一的,也就是说只有自己才会等于自己,那么它的equal方法应该相当于它的==,所以它从Object继承过来的方法刚好满足条件了,不需要覆盖。

2.不关心它的“逻辑相等”,既然都不关心了,那还写什么?

3.超类覆盖的equal方法,对子类同样适用的情况下,就不用覆盖了。比如Set实现了从AbstractSet继承的方法,所以它不用去覆盖了。
 

2.何时需要覆盖

1.有的类的equal永远也不会调用的情况,那么应该覆盖equals方法,让它在调用时抛出错误。

2.如果希望定义特有的”逻辑相等”的情况,可以覆盖equals,比如定义市民类,如果两个市民的身份证字段相等,那么两个市民类相等,这样的逻辑。
 

3.覆盖需要遵守的约定

在覆盖equals方法时,必须遵守它的通用约定:

1)自反性: x.equals(x) 必须返回true 。实例自身必然逻辑相等。

2)对称性: x.equals(y) 与 y.equals(x) 返回结果应该相同,同为true或者同为false

3)传递性: x.equals(y)返回true,y.equals(z)返回true,则x.equals(z)应该返回true。

4)一致性: 只要比较的实例对象的关键属性值没有改变 ,那么无论调用多少次equals方法返回的结果都应该相同,一致。

5)对于非null的x实例,x.equals(null) 永远返回false。
 

这一章的内容十分地充实,这里,我只能总结一下书中重要的内容:

1.不要企图让一个类和一个它的非子类进行equal对比,比如 市民类.equal(String identity),
希望只要市民的身份证号等于字符串类型的identity就返回true,这样是不可行的,因为必然会违反对称性,除非你能去修改String类?

2.我们无法在扩展可实例化的类的同时,既增加新的值组件,同时又保留equal约定。
也就是遇到可实例化的父类和子类,而且子类扩展了父类的问题时,会遇到很尴尬的问题,这里应该认真看一下书~

书中写到了父类和子类的各种纠缠~解决的一种方法,也可以说是权宜之计,就是割断父子关系。。
也就是利用复合优于继承的原则;原来的子类不是扩展了父类吗,那么我让父类成为原来子类的一个私有域,利用复合的方式组装一个类出来。既然不存在父子关系了,父子进行对比产生的种种纠缠也就不复存在了。

书中也有举到一个很有意思的例子,TimeStamp对Date进行扩展,增加了nanoseconds域,由于上面的规则“我们无法在扩展可实例化的类的同时,既增加新的值组件,同时又保留equal约定”,所以设计师也无法进行正确的操作,于是TimeStamp有个免责声明,告诫开发者不要同时使用TimeStamp和Date,也就是不让程序员纠缠在父子的问题上了。

这里有个里式替换原则,可以注意一下:一个类型的任何重要属性也将适用于它的子类型,因此为该类型编写的任何方法,在他的子类型也应该同样运行得很好
 

4.如何使用?


1.先用“==”操作符号判断“参数是否为这个对象的引用”,如果是,那直接返回true。
这其实是一种性能优化,不进行这个操作也会得到一样的结果,所以需要根据每次比较的代价判断是否先用“==”,代价大就需要优化。

2.用instanceof检查“参数是否为正确的类型”。

3.再将参数object对象转成正确的类型。

4.自己定义“逻辑相等”。

5.写完有仔细思考有没有满足对称性,传递性,一致性。

例子:

public class Citizen {    private String name ;    private String Identity;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public String getIdentity() {        return Identity;    }    public void setIdentity(String identity) {        Identity = identity;    }    @Override    public boolean equals(Object obj) {        if(this == obj) {            return true;        }        if(!(obj instanceof Citizen)) {            return false;        }        Citizen citizen = (Citizen)obj;        return this.getIdentity().equals(citizen.getIdentity());    }}

这里的 先用“==”操作符号判断“参数是否为这个对象的引用” 可以不需要。

阅读全文
1 0
原创粉丝点击