通常继承最终会以创建一系列类收场所有类都建立在统一的接口基础上我们用一幅颠倒的树形图来阐明这一点(见注释) [注释] 这儿采用了统一记号法 对这样的一系列类我们要进行的一项重要处理就是将衍生类的对象当作基础类的一个对象对待这一点是非常重要的因为它意味着我们只需编写单一的代码令其忽略类型的特定细节只与基础类打交道这样一来那些代码就可与类型信息分开所以更易编写也更易理解此外若通过继承增添了一种新类型如三角形那么我们为几何形状新类型编写的代码会象在旧类型里一样良好地工作所以说程序具备了扩展能力具有扩展性 以上面的例子为基础假设我们用Java写了这样一个函数 void doStuff(Shape s) { serase(); // sdraw(); } 这个函数可与任何几何形状(Shape)通信所以完全独立于它要描绘(draw)和删除(erase)的任何特定类型的对象如果我们在其他一些程序里使用doStuff()函数 Circle c = new Circle(); Triangle t = new Triangle(); Line l = new Line(); doStuff(c); doStuff(t); doStuff(l); 那么对doStuff()的调用会自动良好地工作无论对象的具体类型是什么 这实际是一个非常有用的编程技巧请考虑下面这行代码 doStuff(c); 此时一个Circle(圆)句柄传递给一个本来期待Shape(形状)句柄的函数由于圆是一种几何形状所以doStuff()能正确地进行处理也就是说凡是doStuff()能发给一个Shape的消息Circle也能接收所以这样做是安全的不会造成错误 我们将这种把衍生类型当作它的基本类型处理的过程叫作Upcasting(上溯造型)其中cast(造型)是指根据一个现成的模型创建而Up(向上)表明继承的方向是从上面来的——即基础类位于顶部而衍生类在下方展开所以根据基础类进行造型就是一个从上面继承的过程即Upcasting 在面向对象的程序里通常都要用到上溯造型技术这是避免去调查准确类型的一个好办法请看看doStuff()里的代码 serase(); // sdraw(); 注意它并未这样表达如果你是一个Circle就这样做如果你是一个Square就那样做等等若那样编写代码就需检查一个Shape所有可能的类型如圆矩形等等这显然是非常麻烦的而且每次添加了一种新的Shape类型后都要相应地进行修改在这儿我们只需说你是一种几何形状我知道你能将自己删掉即erase()请自己采取那个行动并自己去控制所有的细节吧 动态绑定 在doStuff()的代码里最让人吃惊的是尽管我们没作出任何特殊指示采取的操作也是完全正确和恰当的我们知道为Circle调用draw()时执行的代码与为一个Square或Line调用draw()时执行的代码是不同的但在将draw()消息发给一个匿名Shape时根据Shape句柄当时连接的实际类型会相应地采取正确的操作这当然令人惊讶因为当Java编译器为doStuff()编译代码时它并不知道自己要操作的准确类型是什么尽管我们确实可以保证最终会为Shape调用erase()为Shape调用draw()但并不能保证为特定的CircleSquare或者Line调用什么然而最后采取的操作同样是正确的这是怎么做到的呢? 将一条消息发给对象时如果并不知道对方的具体类型是什么但采取的行动同样是正确的这种情况就叫作多形性(Polymorphism)对面向对象的程序设计语言来说它们用以实现多形性的方法叫作动态绑定编译器和运行期系统会负责对所有细节的控制我们只需知道会发生什么事情而且更重要的是如何利用它帮助自己设计程序 有些语言要求我们用一个特殊的关键字来允许动态绑定在C++中这个关键字是virtual在Java中我们则完全不必记住添加一个关键字因为函数的动态绑定是自动进行的所以在将一条消息发给对象时我们完全可以肯定对象会采取正确的行动即使其中涉及上溯造型之类的处理 抽象的基础类和接口 设计程序时我们经常都希望基础类只为自己的衍生类提供一个接口也就是说我们不想其他任何人实际创建基础类的一个对象只对上溯造型成它以便使用它们的接口为达到这个目的需要把那个类变成抽象的——使用abstract关键字若有人试图创建抽象类的一个对象编译器就会阻止他们这种工具可有效强制实行一种特殊的设计 亦可用abstract关键字描述一个尚未实现的方法——作为一个根使用指出这是适用于从这个类继承的所有类型的一个接口函数但目前尚没有对它进行任何形式的实现抽象方法也许只能在一个抽象类里创建继承了一个类后那个方法就必须实现否则继承的类也会变成抽象类通过创建一个抽象方法我们可以将一个方法置入接口中不必再为那个方法提供可能毫无意义的主体代码 interface(接口)关键字将抽象类的概念更延伸了一步它完全禁止了所有的函数定义接口是一种相当有效和常用的工具另外如果自己愿意亦可将多个接口都合并到一起(不能从多个普通class或abstract class中继承) |