- 写在前面的话
- 引言
- 第 1 章 对象入门
- 第 2 章 一切都是对象
- 第 3 章 控制程序流程
- 第 4 章 初始化和清除
- 第 5 章 隐藏实施过程
- 第 6 章 类再生
- 第 7 章 多形性
- 第 8 章 对象的容纳
- 第 9 章 违例差错控制
- 第 10 章 Java IO 系统
- 第 11 章 运行期类型鉴定
- 第 12 章 传递和返回对象
- 第 十三 章 创建窗口和程序片
- 第 14 章 多线程
- 第 15 章 网络编程
- 第 16 章 设计范式
- 第 17 章 项目
- 附录 A 使用非 JAVA 代码
- 附录 B 对比 C++和 Java
- 附录 C Java 编程规则
- 附录 D 性能
- 附录 E 关于垃圾收集的一些话
- 附录 F 推荐读物
11.1 对 RTTI 的需要
请考虑下面这个熟悉的类结构例子,它利用了多形性。常规类型是 Shape 类,而特别衍生出来的类型是 Circle,Square 和 Triangle。
这是一个典型的类结构示意图,基础类位于顶部,衍生类向下延展。面向对象编程的基本目标是用大量代码控制基础类型(这里是 Shape)的句柄,所以假如决定添加一个新类(比如 Rhomboid,从 Shape 衍生),从而对程序进行扩展,那么不会影响到原来的代码。在这个例子中,Shape 接口中的动态绑定方法是 draw(),所以客户程序员要做的是通过一个普通 Shape 句柄调用 draw()。draw() 在所有衍生类里都会被覆盖。而且由于它是一个动态绑定方法,所以即使通过一个普通的 Shape 句柄调用它,也有表现出正确的行为。这正是多形性的作用。
所以,我们一般创建一个特定的对象(Circle,Square,或者 Triangle),把它上溯造型到一个 Shape(忽略对象的特殊类型),以后便在程序的剩余部分使用匿名 Shape 句柄。
作为对多形性和上溯造型的一个简要回顾,可以象下面这样为上述例子编码(若执行这个程序时出现困难,请参考第 3 章 3.1.2 小节“赋值”):
//: Shapes.java package c11; import java.util.*; interface Shape { void draw(); } class Circle implements Shape { public void draw() { System.out.println("Circle.draw()"); } } class Square implements Shape { public void draw() { System.out.println("Square.draw()"); } } class Triangle implements Shape { public void draw() { System.out.println("Triangle.draw()"); } } public class Shapes { public static void main(String[] args) { Vector s = new Vector(); s.addElement(new Circle()); s.addElement(new Square()); s.addElement(new Triangle()); Enumeration e = s.elements(); while(e.hasMoreElements()) ((Shape)e.nextElement()).draw(); } } ///:~
基础类可编码成一个 interface(接口)、一个 abstract(抽象)类或者一个普通类。由于 Shape 没有真正的成员(亦即有定义的成员),而且并不在意我们创建了一个纯粹的 Shape 对象,所以最适合和最灵活的表达方式便是用一个接口。而且由于不必设置所有那些 abstract 关键字,所以整个代码也显得更为清爽。
每个衍生类都覆盖了基础类 draw 方法,所以具有不同的行为。在 main() 中创建了特定类型的 Shape,然后将其添加到一个 Vector。这里正是上溯造型发生的地方,因为 Vector 只容纳了对象。由于 Java 中的所有东西(除基本数据类型外)都是对象,所以 Vector 也能容纳 Shape 对象。但在上溯造型至 Object 的过程中,任何特殊的信息都会丢失,其中甚至包括对象是几何形状这一事实。对 Vector 来说,它们只是 Object。
用 nextElement() 将一个元素从 Vector 提取出来的时候,情况变得稍微有些复杂。由于 Vector 只容纳 Object,所以 nextElement() 会自然地产生一个 Object 句柄。但我们知道它实际是个 Shape 句柄,而且希望将 Shape 消息发给那个对象。所以需要用传统的"(Shape)"方式造型成一个 Shape。这是 RTTI 最基本的形式,因为在 Java 中,所有造型都会在运行期间得到检查,以确保其正确性。那正是 RTTI 的意义所在:在运行期,对象的类型会得到鉴定。
在目前这种情况下,RTTI 造型只实现了一部分:Object 造型成 Shape,而不是造型成 Circle,Square 或者 Triangle。那是由于我们目前能够肯定的唯一事实就是 Vector 里充斥着几何形状,而不知它们的具体类别。在编译期间,我们肯定的依据是我们自己的规则;而在编译期间,却是通过造型来肯定这一点。
现在的局面会由多形性控制,而且会为 Shape 调用适当的方法,以便判断句柄到底是提供 Circle,Square,还是提供给 Triangle。而且在一般情况下,必须保证采用多形性方案。因为我们希望自己的代码尽可能少知道一些与对象的具体类型有关的情况,只将注意力放在某一类对象(这里是 Shape)的常规信息上。只有这样,我们的代码才更易实现、理解以及修改。所以说多形性是面向对象程序设计的一个常规目标。
然而,若碰到一个特殊的程序设计问题,只有在知道常规句柄的确切类型后,才能最容易地解决这个问题,这个时候又该怎么办呢?举个例子来说,我们有时候想让自己的用户将某一具体类型的几何形状(如三角形)全都变成紫色,以便突出显示它们,并快速找出这一类型的所有形状。此时便要用到 RTTI 技术,用它查询某个 Shape 句柄引用的准确类型是什么。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论