多态类型——静中之动
1.多态与继承有何关系?
继承的主要用途不是代码重用,而是代码被重用。这依赖于两个前提,一个是在语义上遵循里式代换原则,另一个是在语法上支持多态机制。
对于静态类型语言来说,继承是多态的基础,多态是继承的目的。
对于动态类型语言来说,鸭子类型是一种不依赖于继承的多态类型,也是动态类型语言一大优劣参半的特点。
2.多态的重要意义何在?多态有哪几种形式?它们各自有什么特点?
静态类型语言中的多态是动静结合的产物,将静态类型的安全性和动态类型的灵活性融为一体。它有两种实现方法:
一种是利用GP(泛型编程)中的参数多态(parametric polymorphism)
一种是利用OOP中的包含多态(inclusion polymorphism)或称子类型多态(subtyping polymorphism)。
从实现机制机制上看,二者的不同之处在于何时将一个变量与其实际类型所定义的行为挂钩。
前者在编译期进行类型绑定,属于早期绑定(early binding)或静态绑定(static binding)。
后者在运行期进行类型决断,属于后期绑定(late binding)或动态绑定(dynamic binding)。
在应用形式上看:
参数多态是发散式的,让相同的实现代码应用于不同的场合。
包含多态是收敛式的,让不同的实现代码应用于相同的场合。
在思维方式/编程范式上看:
前者是泛型式编程风格,看重的是算法的普适性。
后者是对象式编程风格,看重的是接口与实现的分离度。
尽管两者从范式到语法、语义都大相近庭,但都是为着同一个目的:在保证必要的类型安全的前提下,突破编译期间过于严苛的类型限制。对于既是静态类型语言又是静态语言、既支持OOP又支持GP的C++、Java和C#而言,多态机制是保证代码的灵活性、可维护性和可重用性的终极武器。
抽象类型——实中之虚
1.具体类型与抽象类型的区别是什么?
具体类型是创建对象的模板,抽象类型是创建类型的模块。一个是为对象服务的,一个是为类型服务的。显然,后者的抽象性正是源自其服务对象的抽象性。
2.抽象数据类型与抽象类型的区别是什么?抽象类型的主要作用是什么?
抽象数据类型的核心是数据抽象,而抽象类型的核心是多态抽象。 在java和C#中抽象类型只有接口与抽象类,但在C++中这两种并没有什么显式的区别。接口的主要目的是创建多态类型,本事不含任何实现。子类型通过接口继承只能让代码被重用,却无法重用超类型的实现代码。抽象类可以重用代码,可又有多重继承的问题。Java和C#不支持这种机制,C++虽支持但有不少弊端。
3.接口与抽象类在语法和语义上各有什么不同?
Java与C#的抽象类与接口在语法上的区别
抽象类 接口
提供实现代码 能 Java能 C#否
多重继承 否 能
拥有非public成员 能 否
拥有域成员 能 否(java中的static final域成员除外)
拥有static成员 能 否(java中的static final域成员除外)
拥有非abstract方法成员 能 否
方法成员的默认修饰符 无 public abstract(Java:可选。C#:不能含有任何修饰符)
域成员的默认修饰符 吴 java:public static final。 C#:不允许域成员
抽象类的意义就在于:父类推迟决定,让子类选择实现方式。‘推迟’二字道出了抽象类型出创建类型之外的另一个功能:提供动态节点。如果是具体类型,节点已经固定,没有太多变化的余地。反过来,要使节点动态化,一般通过多态来实现。由此可见,抽象类型常常与多态机制形影不离。
Java与C#的抽象类与接口在语义上的区别
接口 抽象类
关系 can-do is-a
共性 相同功能 相同种类
特征 边缘特征 核心特征
联系 横向联系 纵向联系
重用 规范重用 代码重用
实现 多种实现 多级实现
重点 可置换性 可拓展性
演变 新增类型 新增成员
接口是一套功能规范集合,因此相同的接口代表相同的功能,多表示‘can-do’关系,常用后缀为‘able’的形容词命名,如Comparable、Runnable、Cloneable,等等。
接口一般表述的是对象的边缘特征,但也可能描述对象的核心特征,但一个类至多一个这样的接口。
抽象类是一类对象的本质属性的抽象,因此相同的抽象基类代表相同的种类,多表示‘is-a’关系,常用名词命名。
抽象类一般表述的是对象的核心特征,故而只能是一种基类。
从目的性上来看,接口是为了规范重用,让一个规范有多种实现,看重的是可置换性。抽象类主要是为了代码重用(由子类继承同时也能继承接口,抽象类也能规范重用,但更侧重代码重用),能逐级分步实现基类的抽象方法,着重的是可拓展性。
严格来说演变不属于语义范畴, 属于语法推理。在系统演变过程中,接口与抽象类的表现差异巨大。接口由于是广泛采用的规范,相对于行业标准,一经确立不能轻易改动。一旦比广泛采用,它的任何改动(增减接口,修改接口的签名或规范)将波及整个系统,必须慎重。抽象类的演变则没有那么困难,一则它是系统中用得没有接口那么广泛;二则它可随时新增域成员或有默认实现的方法成员,所有子类型将自动得以扩充。这是抽象类的最大优点之一。不过接口也有抽象类所不具备的优点,虽然自身难以演化,但很容易让其他类型演化为该接口的子类型。
4.标记接口有何作用?
标记接口除了能定义类型外,还可作为类型元数据的载体。