目录
表示空间、抽象空间、AF
表示值构成的空间(R/表示空间):实现者看到和使用的值。
抽象值构成的空间(A/抽象空间):client看到和使用的值
Note.ADT开发者关注表示空间R,client关注抽象空间A。
抽象函数:R和A之间映射关系的函数,即如何将R中的每一个值解释为A中的每一个值。
AF:R→A
AF满足以下几个:
1.满射:A中每一个值都有一个或多个映射
2.未必单射:可能R多个值对应A的一个值
3.未必双射(一一映射)
R中的部分值并非合法的,在A中无映射值。
以注释的形式撰写AF、RI
不同的内部表示,需要设计不同的AF和RI
选择某种特定的表示方式R,进而指定某个子集是“合法”的(RI),并为该子集中的每个值做出“解释”(AF)–即如何映射到抽象空间中的值。
同样的表示空间R,可以有不同的RI
即使是相同的R、同样的RI,也可以有不同的AF,即“解释不同”
设计ADT:
- 选择R和A;
- RI–合法的表示值
- 如何解释合法的表示值–映射AF
做出具体的解释:每个rep value如何映射到abstract value。而且把这种选择和解释明确写到代码当中。
随时检查RI是否满足:checkRep()
有益的可变性:对immutable的ADT来说,它在A空间的abstract value应是不变的,但其内部表示的R空间中的取值则可以是变化的。
在代码中用注释形式记录AF和RI:不能在Javadoc文档中写,防止被外部看到而破坏表示独立性/信息隐藏
- 要精确的记录RI:rep中的所有fields何为有效
- 要精确的记录AF:如何解释每一个R值
- 表示泄露的声明:给出理由,证明代码并未对外泄露其内部表示
接口、抽象类、具体类
类:实现ADT。
接口:确定ADT的规约。接口之间可以继承与扩展;一个类可以实现多个接口;一个接口可以有多种实现类
Interface和Class:定义和实现ADT
也可以不需要接口直接使用类作为ADT,既有ADT定义也有ADT实现。(更偏向使用接口来定义变量)
接口优点:
- Safe from bugs
- Easy to understand
- Ready for change
抽象方法:只有定义没有实现。
抽象类:具有抽象方法的类;抽象类不能实例化。(不能用new生成对象)
具体类:其中的所有方法都必须实现!!
继承、override
Strict Inheritance(严格继承):子类只能添加新的方法,无法重写超类中的方法。(final)
public class Car{
public final void drive(){...}//不能在子类重写
}
重写/覆盖的函数:完全同样的signature。(形参,函数名,返回类型)
实际执行时调用哪个方法,运行时决定
ps:1.父类型中被重写函数体不能为空:意味着对其大多数子类型来说,该方法是可以被直接复用的;对某些子类型来说,有特殊性,故重写父类型的函数,实现自己的特殊要求。
2.如果父类型中的某个函数实现体为空,意味着其所有子类型都需要这个功能,但各有差异,没有共性,在每个子类中均需要重写。
3.overriden methods是在run-time进行动态类型检查。
Note.重写之后,利用super()复用父类型中函数的功能,并对其进行扩展。eg.super.message();重写的时候,不要改变原方法的本意。
ps:继承某个抽象类的子类在实例化时,所有父类中的抽象方法必须已经实现
Note.
- 如果某些操作时所有子类型都共有,但彼此有差别,可以在父类型中设计抽象方法,在各子类型中重写。
- 所有子类型完全相同的操作,放在父类型中实现,子类型中无需重写。
- 有些子类型有而其他子类型无的操作,不要在父类型中定义和实现,而应在特定子类型中实现。
多态、overload
多态(polymorphism):
- 特殊(Ad hoc)多态:一个方法可以有多个同名的实现(方法/功能重载);
- 参数化(Parametric)多态:一个类型名字可以代表多个类型(泛型编程);
- 子类型多态、包含(Subtyping)多态:一个变量名字可以代表多个类的实例(子类型);
重载overload(特殊多态):多个方法具有同样的名字,但有不同的参数列表或返回值类型。
overload的价值:方便client调用,client可以用不同的参数列表,调用同样的函数。
Overloading is a static polymorphism(重载是静态多态):根据参数列表进行最佳匹配;静态类型检查;在编译阶段时决定要具体执行哪个方法。
overloading rules:
- 不同的参数列表
- 相同/不同的返回值类型
- 相同/不同的public/private/protected
- 可以定义相同/不同的异常
- 可以子啊同一个类内重载,也可以在子类中重载
参数多态性是指方法针对多种类型(具有通用的结构)时具有相同的行为,此时可使用统一的类型表达多种类型;在运行时根据具体指定类型确定(编译成class文件时,会用指定类型替换类型变量)
泛型
泛型编程是一种编程风格,其中数据类型和函数是根据待定的类型编写的,随后在需要时根据参数提供的特定类型进行实例化。
ps:类中如果声明了一个或多个泛型变量,则为泛型类;泛型接口(字面意思)
Note.通配符:只在使用泛型的时候出现,不能在定义中出现。
eg.List<?> list=new ArrayList();
List<? extedns Animal>
List<? super Animal>
子类型多态:不同类型的对象可以统一的处理而无需区分,从而隔离了“变化”
等价性equals()和==
基于抽象函数AF定义ADT的等价操作,如果AF映射到同样的结果,则等价
==:引用(地址)等价性;对基本数据类型,使用等号判定相等
equals():对象等价性;对对象类型,使用equals
ps:如果对象数据类型使用等号判断是否相等,是在判断两个身份对象身份表示ID是否相等(指向内存里的同一段空间)
Note.instanceof:进行类型比较和null值判定;判断某个对象是不是特定类型(或其子类型);动态类型检查
ps:严格来说,在没有AF的情况下直接在equals()中判断每个域的等价性,是不正确的;
equals()的自反、传递、对称
用“是否为等价关系”检验重写的equals()是否正确
hashCode()
程序中多次调用同一个对象的hashCode方法,都要返回相同的值
等价的对象必须有相同的hashCode(两个equal的objects,一定要有同样的hashCode)
不可变对象的引用等价性、对象等价性
不可变类型必须重写equals()和hashCode()。
可变对象的观察等价性、行为等价性
注意:如果某个mutable对象包含Set集合类中,当其发生改变后,集合类的行为不确定。
对可变类型,实现行为等价即可(equals()should implement behavioral equality)。就是说,只有指向同样内存空间的objects,才是相等的。
对可变类型来说,无需重写equals()和hashCode(),直接继承Object的两个方法即可