等价关系
自反、传递、对称
immutable types的等价性
如果AF映射到同样的结果,则等价
这里区分引用等价和对象等价
== vs. equals()
==说的是引用等价,说的是两个引用指向了相同的内存空间
equals()说的是对象等价,说的是两个对象的field是相同的
重写equals方法
在自定义ADT时需要重写object的equals方法
在object中实现的equals方法是在判断引用等价性
就像这样:
但是这个例子不是重写,而是重载,这个时候其实我们就有两个equals方法,是静态分派了
更好的方法
instanceof
instanceof用于判断某个子类是不是某个类的对象
是动态检查
除了equals方法,任何地方都不应该使用
object的契约
关于equals方法
equals必须定义一个等价关系
x.equals(x) 返回值为true
x.equals(y) 和y.equals(x) 返回值必须一样
传递关系一样的道理
调用多次equals方法应该得到同样的结果
x.equals(null) 的返回值必须是null
有一点需要保证,就是hashcode方法必须和equals方法是同步的,也就是说两个对象相等的时候必须有相同的hashcode返回值
关于hashcode方法
等价的对象必须有相同的hashcode【也就是说如果你重写了equals方法,就必须重写hashcode方法】
不相等的对象,也可以映射为同样的hashcode,但性能会变差【也就是说不相同的对象应该有不同的哈希码】
如果object没有被mutate,那么它的哈希码不能被更改
如果两个相同的对象的哈希码不一样的话,那么将他作为key进行查找的时候就有可能计算出两个哈希值,那么就会查找失败
默认的哈希值是this的内存地址
如果返回一个常量,是符合equals的标准的,但是,所有的存贮对象都会记载一个槽里,这样会增加搜索时间
equality of mutable types
当通过观察无法区分两个对象的时候,我们就说这两个对象是等价的
这里分观察等价性和行为等价性
观察等价性:在不改变状态的情况下【也就是没有调用mutator方法】,两个mutable对象是否看起来一致
行为等价性:调用对象的任何方法都展示出一致的结果【这个的要求比观察等价性更高一些,要求在状态改变的时候也无法分辨出来】
note:对于immutable类型的对象来说,这两个等价性是相同的,因为没有mutator方法
在java中
对可变类型来说,往往倾向于实现严格的观察等价性
但是在有些时候,观察等价性可能导致bug,甚至可能破坏RI(当equals和hashcode可能被改变的时候,可能RI就被改了)
提示:如果某个mutable的对象包含在集合类中,当其(指的是这个对象)发生改变后,集合类的行为不确定
在jdk中,不同的mutable类使用不同的等价性标准(比如collections使用的是观察等价,其他的类比如stringbuilder使用的是行为等价)
设计准则
对于可变类型,实现行为等价性,只有指向同样内存空间的object才是相等的
所以对于可变类型,不需要重写这两个函数
如果一定要判断两个变量看起俩是否一致,最好定义一个新的方法