8 Equality in ADT and OOP ADT和OOP中的“等价性”
Objective of this lecture本章大纲
§ Understand the properties of an equivalence relation. 等价关系
§ Understand equality for immutable types defined in terms of the abstraction function and observations. 站在观察者角度,利用AF,定义不可变对象之间的等价关系
§ Differentiate between reference equality and object equality. 引用等价性和对象等价性
§ Differentiate between strict observational and behavioral equality for mutable types. 可变数据类型的观察等价性和行为等价性
§ Understand the Object contract and be able to implement equality correctly for mutable and immutable types. 理解Object的契约,正确实现等价关系判定
§ Equivalence Relation
§ Equality of Immutable Types
§ == vs. equals()
§ Equality of immutable types
§ The Object contract
§ Equality of Mutable Types
§ Autoboxing and Equality
1 Equivalence Relation
相等就是等价关系中的一个类型
等价关系:自反、对称、传递
2 Equality of Immutable Types
immutable值相等就是等价关系;mutable不能这么比较(可能会发生值的改变)
判断相等时是在A空间判断相等!
a equals b if and only if f(a)=f(b). AF映射到同样的结果,则等价
站在外部观察者角度:对两个对象调用任何相同的操作,都会得到相同的结果,则认为这两个对象是等价的。反之亦然!
比如: Consider the set expressions {1,2} and {2,1}. Using the observer operations available for sets, cardinality |…| and membership ∈, these expressions are indistinguishable:
• |{1,2}| = 2 and |{2,1}| = 2
• 1 ∈ {1,2} is true, and 1 ∈ {2,1} is true
• 2 ∈ {1,2} is true, and 2 ∈ {2,1} is true
• 3 ∈ {1,2} is false, and 3 ∈ {2,1} is false
In terms of ADT, “observation” means calling operations on the objects.
都对,因为没有针对他们的操作
3 == vs. equals()
-
For primitives you must use == 对基本数据类型,使用==判定相等
-
§ For object reference types 对对象类型,使用equals()
– The == operator provides identity semantics 如果用==,比较的是行为相等性,是在判断两个对象身份标识 ID是否相等(指向内存里的同一段空间)
– Exactly as implemented by Object.equals
– Even if Object.equals has been overridden, this is seldom what you want!
– You should (almost) always use .equals
-
Using == on an object reference is a bad smell in code
if (input == “yes”) // A bug!!
4 Implementing equals()
在Object中实现的缺省equals()是在判断引用等价性,通常需要重写
objects o = S;
objects p = xxx;//泛型
o.equals§;
最终会调用实例化后的equals,没有就是调用objects里的(所以上一题答案是false)
直接判断每个域的等价性:会进行类型的强转,这是非常不安全的。
(尤其是不能将子类型转换为父类型)
5 The Object contract
-
除非对象被修改了,否则调用多次equals应同样的结果
-
“相等”的对象,其hashCode()的结果必须 一致( 不相等的对象,也可以映射为同样的hashCode,但性能会变差)
-
“相等”的对象,应当满足等价关系的三个性质:自反、对称、传递
-
for a non-null reference x , x.equals(null) should return false
-
JAVA对于一些mutable是提供观察者类型的equals(如列表类型LIST);对于一些mutable提供的是引用的相等,如Stringbuilder
-
用“是否为等价关系”检验你的equals()是否正确
为避免这种情况,我们需要重写hashCode保证一样的输入获得相同的hashCode;两个equal的objects,一定要 有同样的hashcode
Always override hashCode() when you override equals() 除非你能保证你的ADT不会被放入到Hash类型的集合类中
JAVA一般来讲会自动生成hashCode: Less efficient, but otherwise equally good!(自动生成的hashCode会区分String中的字母大小写!)
6 Equality of Mutable Type
- 观察等价性:在不改变状态的情况下(i.e., by calling only observer, producer, and creator methods),两个mutable对象是否看起来一致
- 行为等价性:调用对象的任何方法(包括mutators)都展示出一致的结果
(对于immutable来讲,观察等价性等价于行为等价性) - 对可变类型来说,往往倾向于实现严格的观察等价性
- Java uses observational equality for most of its mutable data types (such as Collections), but other mutable classes (like StringBuilder ) use behavioral equality.
- If two distinct List objects contain the same sequence of elements, then equals() reports that they are equal
- 这种存在问题,如上图,LIST没法调用到和Set一起引用的同一个值
- 但在有些时候,观察等价性可能导致bug,甚至可能破坏RI(rep invariants )
- objects中提供的equals中的方法实现的是行为等价性
- |A-B|<0.01不满足传递性
- 相等的判断方法:1、AF(a)==AF(b);2、观察等价性:对于两个对象针对他们所提供的任何方法获得的答案都相同
(即行为等价性–>
行为等价性:干脆引用一个对象
clone() in Object
§ clone() creates and returns a copy of this object.
§ The precise meaning of “copy” may depend on the class of the
object.
§ The general intent is that, for any object x:
x.clone() != x
x.clone().getClass() == x.getClass()
x.clone().equals(x)
从这些contracts中无法确保是deep copy!
- 浅拷贝:对于基本类型->拷贝值;对于对象类型->指向原有的一个引用(只新增引用)
7 基本数据类型的封装(打包)Autoboxing and Equality
封装方式:
- ”new":
- “Interger”:number的valueof 最终返回
a.get(“c”).equals(b.get(“c”));:这时候会返回true(👆是false)
单例模式:当值相同时不创建新的对象,指向同一个
(-128~127时使用👇)
a.get(“c”).equals(b.get(“c”));:这时候会返回true(👆是false)
单例模式:当值相同时不创建新的对象,指向同一个
(-128~127时使用👇)
false true(Valueof进行比较)