阅读前建议先熟悉一下可变性与不可变性(6条消息) HIT-SC 可变性与不可变性(JAVA)_Zengwh_02的博客-CSDN博客
一、等价关系
等价关系,即满足自反性、对称性、传递性的一个二元关系
对于一个定义在集合A上的等价关系R
1.自反性:∀ a ∈A => (a, a) ∈ R
2.对称性:(a, b) ∈R∧ a ≠ b => (b, a)∈R
3.传递性:(a, b)∈R,(b, c)∈R =>(a, c)∈R
二、等价测试操作
JAVA中有两种不同的操作来测试对象之间的等价性,一种是“==”,另一种是equals()方法
1.“==”
该测试方法用于测试引用相等,即测试所指向的地址空间是否相等
2.equals()
该测试方法用于测试对象相等
该方法在Object类中就已经被定义,但是Object中的定义与“==”进行的操作相同
public class Object {
public boolean equals(Object that) {
return this == that;
}
}
因此,对于不可变数据类型,需要重写该equals()方法,因为不可变数据类型的等价其实就是内容一样,而不是内存地址一样。
//ImmutableInstance是不可变对象(immutable)
public class ImmutableInstance{
@Override
public boolean equals(Object that) {
return that instanceof ImmutableInstance && this.sameValue((ImmutableInstance)that);
}
private boolean sameValue(ImmutableInstance that) {
...//判断内容是否相同的代码
}
}
此外,由于Object Contract对equals()有要求:
①equals()必须定义等价关系,即自反、对称和传递的关系
②equals()必须一致,即对方法的重复调用必须产生相同的结果
③对于非空引用x,x.equals(null)必须返回false
④hashCode()必须为两个equals()认为等价的对象产生相同的结果
因此,在重写equals()的同时,还必须重写hashCode(),使得满足Object Contract的第④点
而对于可变数据类型,equals()方法往往不能重写,起到一个“==”的作用,因为需要用于判断行为等价性(后面会提到)。若可变数据类型也想有一个测试方法用于内容是否等价,可以单独定义一个类似于上述不可变数据中的sameVlue()方法进行判断。
//MutableInstance是可变对象(mutable)
public class MutableInstance{
private boolean sameValue(MutableInstance that) {
...//判断内容是否相同的代码
}
}
三、不可变数据类型的等价性的判断
1.抽象函数等价性
(补充:抽象函数:AF:R->A。R为表示值空间,A为按照一定映射关系映射到的抽象值空间)
抽象函数等价性:a 等价于 b <=> AF(a) = AF(b),即若两个对象等价,当且仅当以他们为表示值映射到的抽象值相等
2.观察等价性
观察等价性:若两个对象是等价的,当且仅当对两个对象调用所有方法得到的结果都是相同的
注意:这里的方法指的是生产方法(Producer)和观察方法(Observer), 因为不可变数据类型没有变化方法(Mutator)
四、可变数据类型的等价性判断
1.观察等价性
观察等价性:若两个对象是观察等价的,当且仅当两个对象调用所有的生产方法(Producer)和观察方法(Observer)得到的结果都是相同的
2.行为等价性
行为等价性:若两个对象是行为等价的,当且仅当两个对象调用所有的方法得到的结果都是相同的
注意,这里的方法不仅包括所有的生产方法(Producer)和观察方法(Observer),还包括所有变化方法(Mutator)。这就要求这两个对象指向的是同一个地址空间。否则,当一个对象调用mutator方法时,另一个对象未改变,则两者不再相同。