8 Equality in ADT and OOP (ADT和OOP中的"等价性")
1.Equivalence Relation(等价关系)
ADT是对数据的抽象,体现为一组对数据的操作
AF为抽象函数:内部表示--> 抽象表示
我们可以根据抽象函数AF来定义ADT的等价操作:检查抽象函数是否满足等价关系:自反、对称、 传递。
2.Equality of Immutable Types
- a和b是等价的,如果对于抽象函数f(x)有:f(a) =f(b)
- 站在观察者的角度:对于两个对象,调用任何相同的操作,都会得到相同的结果,则认为这两个对象是等价的。
例:对于集合{1,2}和{2,1},有操作 cardinality |...|和membership ∈
对于ADT中所有的方法,我们调用任何方法都不能区别。(如果除了构造方法没有别的方法了,那么我们就不可以通过这一条来判断等价)
例:只有一个构造方法,那么我们就需要通过AF来判断是否等价
Alphabetic case(字母大小写)、non-letters(非字母)
- == 和 equal()
==:对于基本数据类型,判断的是值是否相同。对于引用数据类型,判断的是地址是否相同(是否指向内存中同一块地址)
equal():判断的是对象的等价性。当我们自定义ADT时候,需要重写equals方法。
重写需要@Override注解,不要重载!
例:
为什么d1.equals(d2)为true而d1.equals(o2)为false呐?
因为在Duration中,实际上有两个equals方法
一个为我们上面新加的,另一个为Object类中的equals方法(所有的类均为Obejct的子类)
由于静态类型检查,d1.equals(d2)调用的是我们上面新添加的方法
d1.equals(o2)会因为o2为Object类而调用Object类中的equals方法,实则还是判断地址是否相同。
instanceof关键字:java中的一个双目运算符,可以判断一个对象是否为一个类(或接口、抽象类、父类)的实例,为动态检查
Obj instanceof Class :判断Obj是不是xxx的一个实例
使用:1.声明一个Class类的对象,判断obj是否为Calss的实例对象
2.声明一个Class接口实现类的对象,判断obj是否为Class接口实现类的实例对象
ArrayList arrayList = new ArrayList();
arrayList instanceof List; //为true
3.obj为Class的直接或者间接子类
我们有一个父类Person和继承Person的类Man
注意:obj必须为引用类型,不能为基本类型,否则编译就不会通过
平常我们应该避免使用instanceof,除了再重写equals方法时候。
4.The Object contract
对于equals(),我们必须遵守:
(1).必须符合等价关系
(2).除非对象被修改,否则多次调用equals应该得到同样的结果
(3).“相等的对象”其hashcode()结果也必须一致
5.Equality of Mutable Types
1.观察等价性:在不改变状态的情况下,两个mutable对象是否看起来一致
2.行为等价性:调用对象的任何方法都展现出一致的结果
往往我们更倾向于实现严格的观察等价性。
对于immutable,上述两者是等价的,因为没有 mutator方法
注意:如果某个mutable的对象包含在HashSet集合类中,当其发生改变后,集合类的行为不确定。
例:这是为什么?
一开始,我们的List集合里面只有一个"a",其hashCode我们假设为a
那么会根据其哈希值将其放入一个哈希桶。当list集合添加了一个“b”,那么他的哈希值会改变,不再是a了,但set集合不会因此改变list的哈希桶位置,所以查找就永远不会找到它了。
总结:对于immutable类型,我们要重写equals和hashcode方法
对于mutable类型,我们不应该重写equals和hashcode,但是java在其集合中并没有遵循这一规则,导致我们上述问题的出现。
6.Autoboxing and equality
但是x == y 为false(注意Integer的缓存机制-128-127不适用new Integer的情况)
例:输出结果为false
首先put的时候,会把int的130转换为Integer的130,注意这里因为130不在缓存的范围内,所以a和b放入的不是一个Integer对象,所以两者地址不同,返回false
上述这种情况就是true,因为126在缓存机制的范围之内,所以都指向了同一片内存地址