按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
是安全的;基础类不可能再拥有一个比衍生类更大的接口。因此,我们通过基础类接口发送的每一条消息都
肯定能够接收到。但在进行下溯造型的时候,我们(举个例子来说)并不真的知道一个几何形状实际是一个
圆,它完全可能是一个三角形、方形或者其他形状。
206
…………………………………………………………Page 208……………………………………………………………
为解决这个问题,必须有一种办法能够保证下溯造型正确进行。只有这样,我们才不会冒然造型成一种错误
的类型,然后发出一条对象不可能收到的消息。这样做是非常不安全的。
在某些语言中(如 C++),为了进行保证“类型安全”的下溯造型,必须采取特殊的操作。但在Java 中,所
有造型都会自动得到检查和核实!所以即使我们只是进行一次普通的括弧造型,进入运行期以后,仍然会毫
无留情地对这个造型进行检查,保证它的确是我们希望的那种类型。如果不是,就会得到一个
ClassCastException (类造型违例)。在运行期间对类型进行检查的行为叫作“运行期类型标识”
(RTTI )。下面这个例子向大家演示了RTTI 的行为:
//: RTTI。java
// Downcasting & Run…Time Type
// Identification (RTTI)
import java。util。*;
class Useful {
public void f() {}
public void g() {}
}
class MoreUseful extends Useful {
public void f() {}
public void g() {}
public void u() {}
public void v() {}
public void w() {}
}
public class RTTI {
public static void main(String'' args) {
Useful'' x = {
new Useful();
new MoreUseful()
};
x'0'。f();
x'1'。g();
// pile…time: method not found in Useful:
207
…………………………………………………………Page 209……………………………………………………………
//! x'1'。u();
((MoreUseful)x'1')。u(); // Downcast/RTTI
((MoreUseful)x'0')。u(); // Exception thrown
}
} ///:~
和在示意图中一样,MoreUseful (更有用的)对Useful (有用的)的接口进行了扩展。但由于它是继承来
的,所以也能上溯造型到一个Useful。我们可看到这会在对数组x (位于main()中)进行初始化的时候发
生。由于数组中的两个对象都属于 Useful 类,所以可将 f()和g()方法同时发给它们两个。而且假如试图调
用u() (它只存在于MoreUseful),就会收到一条编译期出错提示。
若想访问一个MoreUseful 对象的扩展接口,可试着进行下溯造型。如果它是正确的类型,这一行动就会成
功。否则,就会得到一个ClassCastException 。我们不必为这个违例编写任何特殊的代码,因为它指出的是
一个可能在程序中任何地方发生的一个编程错误。
RTTI 的意义远不仅仅反映在造型处理上。例如,在试图下溯造型之前,可通过一种方法了解自己处理的是什
么类型。整个第 11章都在讲述 Java 运行期类型标识的方方面面。
7。9 总结
“多形性”意味着“不同的形式”。在面向对象的程序设计中,我们有相同的外观(基础类的通用接口)以
及使用那个外观的不同形式:动态绑定或组织的、不同版本的方法。
通过这一章的学习,大家已知道假如不利用数据抽象以及继承技术,就不可能理解、甚至去创建多形性的一
个例子。多形性是一种不可独立应用的特性(就象一个 switch 语句),只可与其他元素协同使用。我们应将
其作为类总体关系的一部分来看待。人们经常混淆 Java 其他的、非面向对象的特性,比如方法过载等,这些
特性有时也具有面向对象的某些特征。但不要被愚弄:如果以后没有绑定,就不成其为多形性。
为使用多形性乃至面向对象的技术,特别是在自己的程序中,必须将自己的编程视野扩展到不仅包括单独一
个类的成员和消息,也要包括类与类之间的一致性以及它们的关系。尽管这要求学习时付出更多的精力,但
却是非常值得的,因为只有这样才可真正有效地加快自己的编程速度、更好地组织代码、更容易做出包容面
广的程序以及更易对自己的代码进行维护与扩展。
7。10 练习
(1) 创建Rodent (啮齿动物):Mouse (老鼠);Gerbil (鼹鼠);Hamster (大颊鼠)等的一个继承分级结
构。在基础类中,提供适用于所有 Rodent 的方法,并在衍生类中覆盖它们,从而根据不同类型的Rodent 采
取不同的行动。创建一个Rodent 数组,在其中填充不同类型的 Rodent,然后调用自己的基础类方法,看看
会有什么情况发生。
(2) 修改练习 1,使Rodent 成为一个接口。
(3) 改正WindError。java 中的问题。
(4) 在GreenhouseControls。java 中,添加Event 内部类,使其能打开和关闭风扇。
208
…………………………………………………………Page 210……………………………………………………………
第 8 章 对象的容纳
“如果一个程序只含有数量固定的对象,而且已知它们的存在时间,那么这个程序可以说是相当简单的。”
通常,我们的程序需要根据程序运行时才知道的一些标准创建新对象。若非程序正式运行,否则我们根本不
知道自己到底需要多少数量的对象,甚至不知道它们的准确类型。为了满足常规编程的需要,我们要求能在
任何时候、任何地点创建任意数量的对象。所以不可依赖一个已命名的句柄来容纳自己的每一个对象,就象
下面这样:
MyObject myHandle;
因为根本不知道自己实际需要多少这样的东西。
为解决这个非常关键的问题,Java 提供了容纳对象(或者对象的句柄)的多种方式。其中内建的类型是数
组,我们之前已讨论过它,本章准备加深大家对它的认识。此外,Java 的工具(实用程序)库提供了一些
“集合类”(亦称作“容器类”,但该术语已由AWT 使用,所以这里仍采用“集合”这一称呼)。利用这些
集合类,我们可以容纳乃至操纵自己的对象。本章的剩余部分会就此进行详细讨论。
8。1 数组
对数组的大多数必要的介绍已在第 4 章的最后一节进行。通过那里的学习,大家已知道自己该如何定义及初
始化一个数组。对象的容纳是本章的重点,而数组只是容纳对象的一种方式。但由于还有其他大量方法可容
纳数组,所以是哪些地方使数组显得如此特别呢?
有两方面的问题将数组与其他集合类型区分开来:效率和类型。对于Java 来说,为保存和访问一系列对象
(实际是对象的句柄)数组,最有效的方法莫过于数组。数组实际代表一个简单的线性序列,它使得元素的
访问速度非常快,但我们却要为这种速度付出代价:创建一个数组对象时,它的大小是固定的,而且不可在
那个数组对象的“存在时间”内发生改变。可创建特定大小的一个数组,然后假如用光了存储空间,就再创
建一个新数组,将所有句柄从旧数组移到新数组。这属于“矢量”(Vector)类的行为,本章稍后还会详细
讨论它。然而,由于为这种大小的灵活性要付出较大的代价,所以我们认为矢量的效率并没有数组高。
C++的矢量类知道自己容纳的是什么类型的对象,但同 Java 的数组相比,它却有一个明�