ADT和OOP中的等价性
作者:互联网
等价性问题:现实世界中的对象实体都是独一无二的,除了是同一个,不可能完全相等。基于 OOP 原则,在软件中,也要考虑这个相等的问题,我们肯定不能说除了同一个,全都不相等,什么情况下两个事物认为是等价的,可相互替代的,即我们要关注的等价性问题。
等价关系:等价具有自反、对称、传递的性质,我们对等价方法的设计也必须遵守这些性质。
三种认定等价的方式:
1)基于 AF 定义 ADT 的等价操作:如果 AF 将两个对象映射到同样的结果,则这两个对象等价。
2)基于外部观察者角度认定等价性:对于两个对象调用任何相同的操作都得到相同的结果,则认为二者等价。
3)同一个才算是等价:一般是用“==”定义的等价,即引用等价性,只有两个对象在内存中的地址是相同的才会被认定为等价。但注意,“==”对于基本数据类型是直接依据其具体内容是否相同来判断等价的。
对于自己设计的ADT的等价判断方法的实现:
“==”是直接判断对象的内存地址的,不想如此定义等价就要提供一种单独的方法,也即 equals() 方法。Object 类的 equals() 方法是直接用“==”来判断的,若自己设计 ADT,一般都需要根据自己的需求来重写 equals 方法。注意,是重写 equals() 方法,而非重载。这就需要我们传入的参数是 Object 类,并在 equals() 内多一步判断传入的参数对象是否是你所要判断的类,若是,则将其转换成你所要判断的 ADT 的类,再进行具体内容的比较。下图给出一个按上述格式重写 equals() 方法的例子:
但是,仅仅重写 equals() 方法是不够的!若两个对象“相等”,它还必须要有相同的哈希码,也即hashCode()结果必须相同。这是因为我们设计的 ADT 可能被用于如 hashSet、 hashMap 等类型,在判断等价时,可能会先判断 hashCode() 是否相等,若相等,再在同一个哈希桶中找到对应对象进行比较,否则将直接判断为不等。
因此,我们若重写了 equals() 方法,也就需要重写 hashCode() 方法。重写 hashCode() 应该注意,返回值应该由我们判断相等时考虑的那些内容的信息构成,就如上图的例子,返回的哈希码应该是和 name.first 和 name.last 有关的一个 int 值,比如返回 17*hashCode(name.first)+hashCode(name.last) 等。
对于可变数据类型,判断等价性有两种标准:观察等价性和行为等价性。观察等价性即不改变对象的状态对其观察,看是否可以区分,也即对其 creator、producer 和 observer 方法进行测试,不测 mutator 方法;行为等价性则要求在对两个对象改变状态后仍然无法区分,即调用 mutator 方法后,进行别的操作也不会得到不同的结果。可以理解为观察等价性是看当前状态不变时,两个对象是否相等,行为等价性则要求两个对象现在、以后任何时刻,都无法区分。
注意,对于可变数据类型来说,观察等价性可能导致 bug,甚至破坏 RI,比如对于一个hashSet<List<String>> 对象来说,如果将 list 修改,导致 list 的哈希码发生变化,但是它仍然存在在 set 集合的原来的哈希桶中,即在 set 集合中对应的仍然是原来的哈希码,这时,如果调用 set.contains(list) 将返回 false ,即使 list 的确存在在 set 里(contains方法会调用等价方法来判断是否包含对应对象元素)。
由于观察等价性的这个问题,对于可变数据类型,我们应该用行为等价性来判断自己设计的 ADT 的对象等价与否,也即只能直接用“==”来判断,所以我们直接继承 Object 的 equals 方法即可。而对于不可变数据类型,其根本没有 mutator,不会改变状态,所以只有观察等价性标准来判断其是否等价,因此需要重写 equals() 方法和 hashCode() 方法。
标签:ADT,对象,equals,等价,hashCode,OOP,方法 来源: https://www.cnblogs.com/diguawan/p/16365119.html