文章目录
前言
在软件构造中,常常需要判断两个对象是否是等价的。在Java中,由于存在两种数据类型Immutable
和Mutable
,其对于等价的判断也是不相同的。
什么是等价关系呢?等价关系应该满足自反、对称、传递的特性。
有两种定义等价性的角度。
一种是从抽象函数AF的视角,AF映射到同样的结果,则等价;
另一种是站在观察者角度,对两个对象调用任何相同的操作,都会得到相同的结果,则认为这两个对象是等价的。
一、Immutable类型的等价性
在代码中,判断两个对象是否具有等价关系,有两种方式,"=="
和.equals()
方法。两者分别实现了不同的判定方式。
1.1"=="
表达引用等价性
用"=="
来判断等价性,实际上考量的是两个对象在内存中的地址空间是否一致,对于Immutable类型的对象来说这又被称为引用等价性。一般情况下,对于基本数据类型的判断如int, long, double
等都会使用"=="
来判断引用等价性。
如果两个对象是引用等价的,那么这两个对象其实就是一个对象,那么他就会满足所有的等价性。
1.2.equals()
表达对象等价性
用".equals()"
方法来判断等价性,要求就没有"=="
那么高了,也就是说,满足等价的定义即可。两个对象不一定要是同一个对象,只要这两个对象在我们所关心的维度是等价的就行了,所以可以看出,用equals()
方法来判断对象的等价关系是基于实际情况的可以个性化实现的。一般情况下,对于对象类型的数据都会使用equals()
方法来判断等价性。
那么如何基于实际情况个性化实现呢?equals()
方法是继承自Object()
这一父类的,而在Object()
中equals()
方法是实现引用等价性的,这常常不是程序员们所需要的。于是在写一个继承自Object()
父类的ADT时,要对equals()
方法进行重写。
1.3重写equals()方法
我们首先要重写equals()
方法,一定要保证重写后的equals()
方法判断的等价性满足定义以及自反、传递、对称的特性。
但是重写了equals()
方法就够吗?其实是不够的,我们还要重写Object()
父类中的另一个方法HashCode()
方法,这是因为在Object()
父类中规定了如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 HashCode()
方法都必须生成相同的整数结果。基于这一原因,我们就必须要重写HashCode()
保证equals()
判断的等价的两个对象,他们的HashCode也要一致才行。如图。
下面是一个示例。
二、Mutable类型的等价性
对于Mutable类型的对象,常常提到的是观察等价性和行为等价性。之所以不提引用等价性和对象等价性是因为在Mutable类型的对象中,这两种等价性其实是一样的。
2.1观察等价性
观察等价性是指在不改变状态的情况下,两个mutable对象是否看起来一致。对可变类型来说,往往倾向于实现严格的观察等价性。但在有些时候,观察等价性可能导致bug,甚至可能破坏RI,这是因为过段时间后可能两者就不等价了,因为值可能会发生变化。观察等价性同样通过重写equals()
和HashCode()
方法来实现。
2.2行为等价性
行为等价性是指调用对象的任何方法都展示出一致的结果。对可变类型,实现行为等价性即可。也就是说,只有指向同样内存空间的objects,才是相等的。所以对可变类型来说,行为等价无需重写equals()
和HashCode()
这两个函数,直接继承Object()
的两个方法即可。也就是说,对于行为等价的Mutable类型的对象来说,==
和equals()
是一样的。如果一定要判断两个可变对象看起来是否一致,最好定义一个新的方法。
Mutable类型对象equals()
方法示例。
总结
本文总结了对象的等价关系。