购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

1.6 继承

对象本身的理念是提供一种便捷的工具。对象可以根据定义的概念来封装数据和功能,从而展现给人们对应的问题空间的概念,而不是强迫程序员操作机器底层。在编程语言里,这些基础概念通过关键字class得以呈现。

然而,当我们大费周折才创建了一个类之后,如果不得不再创建一个与之前功能极为相近的类,这种滋味一定不太好受。如果我们能够复制现有的类,并且在该复制类的基础上再做一些增补的话,那就太妙了。实际上,这就是继承给我们带来的好处,除了一点:如果最初的类(叫作“基类”“超类”或“父类”)发生了变化,那么被修改的“复制”类(叫作“派生类”“继承类”或“子类”)同样会发生变化(见图1-3)。

图1-3

图1-3中的箭头从子类指向其基类。之后你将看到,子类通常会有多个。

一个类呈现的内容不只是对象能做什么、不能做什么,它还可以关联其他的类。两个类可以拥有相同的行为和特征,但一个类可以比另一个类拥有更多的特征,以及处理更多的消息(或者用不同的方式处理消息)。继承通过基类和子类的概念来表述这种相似性,即基类拥有的所有特征和行为都可以与子类共享。也就是说,你可以通过基类呈现核心思想,从基类所派生出的众多子类则为其核心思想提供了不同的实现方式。

举个例子。一个垃圾收集器需要对垃圾进行分类。我们创建的基类是“垃圾”,具体的每一件垃圾都有各自不同的重量、价值,并且可以被切碎、溶解或者分解等。于是,更为具体的垃圾子类就出现了,并且带有额外的特征(比如,一个瓶子有颜色,一块金属有磁性等)和行为(比如你可以压扁一个铝罐)。此外,有些行为还可以产生不同的效果(比如纸质垃圾的价值取决于它的类型和状态)。通过继承,我们创建了一种“类型层次”(type hierarchy)以表述那些需要根据具体类型来解决的问题。

还有一个常见的例子是形状,你可能在计算机辅助设计系统或模拟游戏中碰过到。具体来说,基类就是“形状”(Shape),而每一个具体的形状都具有大小、颜色、位置等信息,并且可以被绘制(draw())、清除(erase())、移动(move())、着色(getColor或setColor)等。接下来,基类Shape可以派生出特定类型的形状,比如圆形(Circle)、矩形(Square)、三角形(Triangle)等,每一个具体形状都可以拥有额外的行为和特征,比如某些形状可以被翻转(见图1-4)。有些行为背后的逻辑是不同的,比如计算不同形状的面积的方法就各不相同。所以,类型层次既体现了不同类之间的相似性,又展现了它们之间的差异。

图1-4

问题和解决方案都使用相同的表达方式是非常有用的,因为这样就不再需要一个中间模型将问题翻译为解决方案。在面向对象领域,类型层次是该模型的一个重要特征,它让你可以方便地从现实世界中的系统转换到代码世界的系统。不过现实情况是,有些人由于习惯了复杂的解决方案,因此对于面向对象的简约性反而会有些不适应。

继承已有的类将产生新类。这个新的子类不但会继承其基类的所有成员(虽然private成员是隐藏且不可访问的),而且更重要的是,子类也会继承基类的接口。也就是说,所有基类对象能够接收的消息,子类对象也一样能够接收。我们可以通过一个类所接收的消息来确定其类型,所以从这一点来说,子类和基类拥有相同的类型。引用之前的例子,就是“圆形是一个形状”。所以,掌握这种通过继承表现出来的类型相同的特性,是理解面向对象编程的基础方法之一。

既然基类和子类拥有相同的基础接口,就必然存在接口的具体实现。这意味着,当一个对象接收到特定的消息时,就会执行对应的代码。如果你继承了一个类并且不做任何修改的话,这个基类的方法就会原封不动地被子类所继承。也就是说,子类的对象不但和基类具有相同的类型,而且不出所料的是,它们的行为也是相同的。

有两种方法可以区分子类和基类。第一种方法非常简单直接:为子类添加新的方法(见图1-5)。因为这些方法并非来自基类,所以背后的逻辑可能是,基类的行为和你的预期不符,于是你添加了新的方法以满足自己的需求。有时候,继承的这种基础用法能够完美地解决你面临的问题。不过,你需要慎重考虑是否基类也需要这些新的方法(还有一个替代方案是考虑使用“组合”)。在面向对象编程领域里,这种对设计进行发现和迭代的情况非常普遍。

图1-5

虽然有时候继承意味着需要为子类添加新的方法[Java尤其如此,其用于继承的关键字就是“扩展”(extends)],但这不是必需的。还有一种让新类产生差异化的方法更为重要,即修改基类已有方法的行为,我们称之为“重写”该方法(见图1-6)。

图1-6

如果想要重写一个方法,你可以在子类中对其进行重新定义。也就是说,你的预期是“我想通过相同的接口调用该方法,但是我希望它可以在新的类中实现不同的效果”。 gIR9QBy2SDt/0irnreWPDwq1wFTLji8oGlb0OTSeWEI/A2AAADEJCzTF2QATOjH0

点击中间区域
呼出菜单
上一章
目录
下一章
×

打开