三种“相等”的方式
抽象函数:抽象函数将具体的数据映射到抽象的值,如果AF(a)=AF(b),我们就说a和b相等。
等价关系:等价是指对于关系,满足自反性,对称性,传递性。则我们说a等于b当且仅当E(a,b)。
观察:两个对象a和b相等当且仅当使用者无论怎样观察总会得到相同的结果。
== vs .equals()
==比较的是索引。如果两个变量==,那么它们指向同一块存储区域,在快照图中展现为它们的箭头指向同一个对象。
.equals()操作是比较对象的内容。比较的是两个对象的值是否相等。equals方法在是在object方法下定义的,在每个ADT中,都必须对equals函数进行相应的定义。
不可变类型的相等
equals()
是在 Object
中定义的,它的(默认)实现方式如下:
public class Object {
...
public boolean equals(Object that) {
return this == that;
}
}
equals()
在Object
中的实现方法就是测试指向/索引相等。对于不可变类型的对象来说,这几乎总是错的。所以你需要覆写equals()
方法,将其替换为你的实现。
对象契约
在object中有equal
的规格说明。当我们在覆写equals
时,要记得遵守这些规定:
1.equals
必须定义一个等价关系。即一个满足自反性、对称性和传递性关系。
2.equals
必须是确定的。即连续重复的进行相等操作,结果应该相同。
3.对于不是null的索引x
, x.equals(null)
应该返回false。
4.如果两个对象使用 equals
操作后结果为真,那么它们各自的hashCode
操作的结果也应该相同。
可变类型的相等
与不可变类型相比,可变类型可以通过在观察前调用改造者,我们可以改变其内部的状态,从而观察出不同的结果。
所以让我们重新定义两种相等:
- 观察相等:两个索引在不改变各自对象状态的前提下不能被区分。例如,只调用观察者、生产者、创建者。它测试的是这两个索引在当前程序状态下“看起来”相等。
- 行为相等:两个所以在任何代码的情况下都不能被区分,即使有一个对象调用了改造者。它测试的是两个对象是否会在未来所有的状态下“行为”相等。+
对于不可变对象,观察相等和行为相等是完全等价的,因为它们没有改造者改变对象内部的状态。
对于可变对象,Java通常实现的是观察相等。但这可能会导致一些非常隐蔽的bug。事实上,对可变类型而言,equals应该实现行为相等,可惜java并没有进行这样的设计。