当前位置:高等教育资讯网  >  中国高校课件下载中心  >  大学文库  >  浏览文档

《Thinking in Java》中文版 第十章 检测类型

资源类别:文库,文档格式:PDF,文档页数:25,文件大小:414.07KB,团购合买
初看起来“运行时类型识别(run-time type identification,缩写为 RTTI)”的想法很简单:要让你在只持有这个对象的基类的 reference的 情况下,找出它的确切的类型。 但是,这种对“RTTI的强烈需求”揭示了许多OO设计中会碰到的有趣 (同时也很令人困惑)的问题,并且引出了“该如何组织程序结构”这一根 本性的问题。
点击下载完整版文档(PDF)

Thinking in Java 3 Edition 10:检测类型 初看起来“运行时类型识别( run-time type identification,缩写为 RTT)”的想法很简单:要让你在只持有这个对象的基类的 reference的 情况下,找出它的确切的类型。 但是,这种对“RTTI的强烈需求”揭示了许多OO设计中会碰到的有趣 (同时也很令人困惑)的问题,并且引出了“该如何组织程序结构”这一根 本性的问题 本章要讲解Java所提供的,让你在运行时发现对象和类的信息的方法。 它有两种形式:一种是假设你在编译和运行时都完全知道类型的具体信息 的“传统的”RTT,另一种是允许你只依靠运行时信息发掘类型信息的 “ reflection”机制。我们先讲“传统的”RTTI,再讨论 reflection。 为什么会需要RTTT 先想一想我们现在已经很熟悉的那个多态性的例子。通用的基类 Shape 派生出具体的 Circle, Square和 Triangle类: Shape draw Circle square Triangle 这是一个典型的类系( class hierarchy)结构图,基类在最上面,派生类 在下面。OOP的目的就是要让你用基类的 reference(这里就是 Shape) 来写程序,因此如果你往程序里面加了一个新的类(比如 Shape又派生 了一个 Rhomboid),绝大多数的代码根本不会受到影响。在这个例子 里面, Shape接口动态绑定的是draw(),于是客户程序员能够通过 通用的 Shape reference来调用draw()。所有派生类都会覆写 draw(),而且由于这个方法是动态绑定的,因此即便是通过通用的 Shape reference来进行的调用,也能得到正确的行为。这就是多态 性 由此,编程时一般会先创建一个具体的对象( Circle, Square,或 Triangle),再把它上传到 Shape(这样就把对象的具体类型给忘了) 然后在余下来的程序里面使用那个匿名的 Shape reference 第1页共17页 www.wgqqh.com/shhgs/tij.html emailshhgsasohu.com

Thinking in Java 3rd Edition 第 1 页 共 17 页 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com 10: 检测类型 初看起来“运行时类型识别(run-time type identification,缩写为 RTTI)”的想法很简单:要让你在只持有这个对象的基类的 reference 的 情况下,找出它的确切的类型。 但是,这种对“RTTI 的强烈需求”揭示了许多 OO 设计中会碰到的有趣 (同时也很令人困惑)的问题,并且引出了“该如何组织程序结构”这一根 本性的问题。 本章要讲解 Java 所提供的,让你在运行时发现对象和类的信息的方法。 它有两种形式:一种是假设你在编译和运行时都完全知道类型的具体信息 的“传统的”RTTI,另一种是允许你只依靠运行时信息发掘类型信息的 “reflection”机制。我们先讲“传统的”RTTI,再讨论 reflection。 为什么会需要 RTTI 先想一想我们现在已经很熟悉的那个多态性的例子。通用的基类 Shape 派生出具体的 Circle,Square 和 Triangle 类: 这是一个典型的类系(class hierarchy)结构图,基类在最上面,派生类 在下面。OOP 的目的就是要让你用基类的 reference(这里就是 Shape) 来写程序,因此如果你往程序里面加了一个新的类(比如 Shape 又派生 了一个 Rhomboid),绝大多数的代码根本不会受到影响。在这个例子 里面,Shape 接口动态绑定的是 draw( ),于是客户程序员能够通过 通用的 Shape reference 来调用 draw( )。所有派生类都会覆写 draw( ),而且由于这个方法是动态绑定的,因此即便是通过通用的 Shape reference 来进行的调用,也能得到正确的行为。这就是多态 性。 由此,编程时一般会先创建一个具体的对象(Circle,Square,或 Triangle),再把它上传到 Shape(这样就把对象的具体类型给忘了), 然后在余下来的程序里面使用那个匿名的 Shape reference

Chapter 10: Detecting Types 作为多态性的简要回顾,我们再用程序来描述一遍上面这个例子 //: c10: Shapes. java import com. bruceeckel. simpletest. c⊥ass Shape void draw()[ System. out. println(this draw()");} class Circle extends Shape public String tostring() return Circle"i) class Square extends Shape public string tostring()i return Square"i J class Triangle extends shape i public String tostring ()( return "Triangl public class Shapes i private static Test monitor new Test()i public static void main(String[ args)I // Array of object, not shape Object[ shapelist = t new circled), () new Triangle( for(int 1=0 Shape)shapelist[i]).draw()i// Must cast monitor. expect(new String[] t le draw( 'Square. draw () Triangle. draw( 基类里有一个间接调用 tostring()来打印类的打印标识符的draw() 方法。它把this传给 System. out println(),而这个方法一看到对 象就会自动调用它的 tostring()方法,这样就能生成对象的 String表 达形式了。每个派生类都会覆写 tostring()方法(从 Object继承下来 的),这样draw()就能(多态地)打印各种对象了 man()创建了一些具体的 Shape对象并且把它们加进一个数组。这个 数组有点怪,因为它不是 Shape(尽管可以这样),而是根类 Object的 数组。这是在为第11章要讲的 collection(也称 container)作铺垫。 collection是一种工具,它只有一种用途,就是要为你保管其它对象。因 此出于通用性的考虑,这些 collection应该能持有任何东西。所以它们持 第2页共17页 www.wgqqh.com/shhgs/tij.html emailshhgsasohu.com

Chapter 10: Detecting Types www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com 第 2 页 共 17 页 作为多态性的简要回顾,我们再用程序来描述一遍上面这个例子: //: c10:Shapes.java import com.bruceeckel.simpletest.*; class Shape { void draw() { System.out.println(this + ".draw()"); } } class Circle extends Shape { public String toString() { return "Circle"; } } class Square extends Shape { public String toString() { return "Square"; } } class Triangle extends Shape { public String toString() { return "Triangle"; } } public class Shapes { private static Test monitor = new Test(); public static void main(String[] args) { // Array of Object, not Shape: Object[] shapeList = { new Circle(), new Square(), new Triangle() }; for(int i = 0; i < shapeList.length; i++) ((Shape)shapeList[i]).draw(); // Must cast monitor.expect(new String[] { "Circle.draw()", "Square.draw()", "Triangle.draw()" }); } } ///:~ 基类里有一个间接调用 toString( )来打印类的打印标识符的 draw( ) 方法。它把 this 传给 System.out.println( ),而这个方法一看到对 象就会自动调用它的 toString( )方法,这样就能生成对象的 String 表 达形式了。每个派生类都会覆写 toString( )方法(从 Object 继承下来 的),这样 draw( )就能(多态地)打印各种对象了。 main( )创建了一些具体的 Shape 对象并且把它们加进一个数组。这个 数组有点怪,因为它不是 Shape(尽管可以这样),而是根类 Object 的 数组。这是在为第 11 章要讲的 collection(也称 container)作铺垫。 collection 是一种工具,它只有一种用途,就是要为你保管其它对象。因 此出于通用性的考虑,这些 collection 应该能持有任何东西。所以它们持

Thinking in Java 3 Edition 有 Object。因此你可以从 Object数组看到一个你将在第11章遇到的 重要问题 在这个例子里,当把 Shape放进 object数组的时候,上传就发生了。 由于Java里面的所有东西(除了 primitive)都是对象,因此 Object数 组也能持有 Shape对象。但是在上传过程中,“这个对象是 Shape 的信息丢失了。对数组而言,它们都只是 object 但是,当你用下标把元素从数组里提取出来的时候,就免不了要忙活一阵 了。由于数组持有的都是些 Object,因此提取出来的也就自然都是 Object的 reference了。但是我们知道,它实际上是 Shape的 reference,而且我们也要向它发送 Shape的消息。于是老套的 “( Shape)”类型转换就变得必不可少了。这是RTTI的最基本的形 式,因为程序运行的时候会逐项检査转换是不是都正确。这种检查正体现 了RTTI的本义:在运行时鉴别对象的类型 这里的RTT转换并不彻底: object只是转换成 Shape,而不是一下 子转换成 Circle, Square或 Triangle。这是因为现阶段我们只知道 数组存储的是 Shape。编译的时候,这种转换要由你自己来把关,但是 运行的时候这种转换就不用你操心了。 接下来就交给多态性了。 Shape到底要执行哪段代码,这要由 reference究竟是指向 Circle, Square还是 Triangle对象来决定。 总之程序就该这么写;你的目标就是,要让绝大多数代码只同代表这个类 系的基类对象打交道(在本案中,就是 Shape),对它们来说,越少知道 下面的具体类型越好。这样一来,代码的读写和维护就会变得更简单,而 实现,理解和修改设计方案也会变得更容易一些。所以多态性是OOP的 个主要目标 但是如果你碰到一个特殊问题,而要解决这个问题,最简单的办法就是 “找出这个通用的 reference究竟是指哪个具体类型的”,那你又该怎 么办呢?比方说,你要让用户能用“把这类对象全都染成紫色”的办法来 突出某种 Shape。或者,你要写一个会用到“旋转”一组 shape的方 法。但是对圆形来说,旋转是没有意义的,所以你只想跳过圆形对象。有 了RTTI,你就能问出 Shape的 reference究竟是指的哪种具体类型的 对象了,这样你就能把特例给区别出来了。 Class对象 想要知道Java的RTTI是如何工作的,你就必须首先知道程序运行的时 候,类型信息是怎样表示的。这是由一种特殊的,保存类的信息的,叫作 “Cass对象( Class object)”的对象来完成的。实际上类的“常规”对 象是由 Class对象创建的 第3页共17页 www.wgqqh.com/shhgs/tij.html emailshhgsasohu.com

Thinking in Java 3rd Edition 第 3 页 共 17 页 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com 有 Object。因此你可以从 Object 数组看到一个你将在第 11 章遇到的 重要问题。 在这个例子里,当把 Shape 放进 Object 数组的时候,上传就发生了。 由于 Java 里面的所有东西(除了 primitive)都是对象,因此 Object 数 组也能持有 Shape 对象。但是在上传过程中,“这个对象是 Shape” 的信息丢失了。对数组而言,它们都只是 Object。 但是,当你用下标把元素从数组里提取出来的时候,就免不了要忙活一阵 了。由于数组持有的都是些 Object,因此提取出来的也就自然都是 Object 的 reference 了。但是我们知道,它实际上是 Shape 的 reference,而且我们也要向它发送 Shape 的消息。于是老套的 “(Shape)”类型转换就变得必不可少了。这是 RTTI 的最基本的形 式,因为程序运行的时候会逐项检查转换是不是都正确。这种检查正体现 了 RTTI 的本义:在运行时鉴别对象的类型。 这里的 RTTI 转换并不彻底:Object 只是转换成 Shape,而不是一下 子转换成 Circle,Square 或 Triangle。这是因为现阶段我们只知道 数组存储的是 Shape。编译的时候,这种转换要由你自己来把关,但是 运行的时候这种转换就不用你操心了。 接下来就交给多态性了。Shape 到底要执行哪段代码,这要由 reference 究竟是指向 Circle,Square 还是 Triangle 对象来决定。 总之程序就该这么写;你的目标就是,要让绝大多数代码只同代表这个类 系的基类对象打交道(在本案中,就是 Shape),对它们来说,越少知道 下面的具体类型越好。这样一来,代码的读写和维护就会变得更简单,而 实现,理解和修改设计方案也会变得更容易一些。所以多态性是 OOP 的 一个主要目标。 但是如果你碰到一个特殊问题,而要解决这个问题,最简单的办法就是 “找出这个通用的 reference 究竟是指哪个具体类型的”,那你又该怎 么办呢?比方说,你要让用户能用“把这类对象全都染成紫色”的办法来 突出某种 Shape。或者,你要写一个会用到“旋转”一组 shape 的方 法。但是对圆形来说,旋转是没有意义的,所以你只想跳过圆形对象。有 了 RTTI,你就能问出 Shape 的 reference 究竟是指的哪种具体类型的 对象了,这样你就能把特例给区别出来了。 Class 对象 想要知道 Java 的 RTTI 是如何工作的,你就必须首先知道程序运行的时 候,类型信息是怎样表示的。这是由一种特殊的,保存类的信息的,叫作 “Class 对象 (Class object)”的对象来完成的。实际上类的“常规”对 象是由 Class 对象创建的

Chapter 10: Detecting Types 程序里的每个类都要有一个clas对象。也就是说,每次你撰写并且编 译了一个新的类的时候,你就创建了一个新的 class对象(而且可以这么 说,这个对象会存储在同名的 class文件里)。程序运行时,当你需要创 建一个那种类的对象的时候,巩M会检查它是否装载了那个 Class对 象。如果没有,巩VM就会去找那个 class文件,然后装载。由此也可知 道,Java程序在启动的时候并没有完全装载,这点同许多传统语言是不 样的。 那种类型的 class对象被装进了内存,所有那个类的对象就都会由 它来创建了。如果这听上去太玄,或者你不相信的话,下面这个程序就能 作证明 77: c10: Sweet shop. java Examination of the way the class loader works import com. bruceeckel. simpletest. class Candy static I System. out. println("Loading Candy") class Gum I System. out. println("Loading Gum") class Cookie i static System. out. println("Loading Cookie")i public class Sweet shop t private static Test monitor new Test()i public static void main(string[] args)t System. out. println("inside main")i new Candy ()i System. out. printin ("After creating candy" )i System. out. println("Couldn't find Gum")i System. out. println(" After Class. f (\"Gum\")") System. out. println ("After creating Cookie monitor. expect (new String[] t Loading Gum", 第4页共17页 www.wgqqh.com/shhgs/tij.html

Chapter 10: Detecting Types www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com 第 4 页 共 17 页 程序里的每个类都要有一个 Class 对象。也就是说,每次你撰写并且编 译了一个新的类的时候,你就创建了一个新的 Class 对象(而且可以这么 说,这个对象会存储在同名的.class 文件里)。程序运行时,当你需要创 建一个那种类的对象的时候,JVM 会检查它是否装载了那个 Class 对 象。如果没有,JVM 就会去找那个.class 文件,然后装载。由此也可知 道,Java 程序在启动的时候并没有完全装载,这点同许多传统语言是不 一样的。 一旦那种类型的 Class 对象被装进了内存,所有那个类的对象就都会由 它来创建了。如果这听上去太玄,或者你不相信的话,下面这个程序就能 作证明: //: c10:SweetShop.java // Examination of the way the class loader works. import com.bruceeckel.simpletest.*; class Candy { static { System.out.println("Loading Candy"); } } class Gum { static { System.out.println("Loading Gum"); } } class Cookie { static { System.out.println("Loading Cookie"); } } public class SweetShop { private static Test monitor = new Test(); public static void main(String[] args) { System.out.println("inside main"); new Candy(); System.out.println("After creating Candy"); try { Class.forName("Gum"); } catch(ClassNotFoundException e) { System.out.println("Couldn't find Gum"); } System.out.println("After Class.forName(\"Gum\")"); new Cookie(); System.out.println("After creating Cookie"); monitor.expect(new String[] { "inside main", "Loading Candy", "After creating Candy", "Loading Gum", "After Class.forName(\"Gum\")", "Loading Cookie

Thinking in Java 3 Edition Candy,Gum,和 Cookie,每个类都有一条“会在类第一次被装载的 时候执行”的 static语句。这样装载的类时候就会它就会打印消息通知 你了。对象的创建被分散到了man()的各条打印语句之间,其目的就 是要帮助你检测装载的时机。 你可以从程序的输出看到, Class对象只会在需要的时候装载,而 static初始化则发生在装载类的时候。 下面这行特别有趣 Class. forName("Gum 这是一个 Class的 static方法(所有的 Class对象所共有的)。 class 对象同其它对象一样,也可以用 reference来操控(这是装载器要干的), 而要想获取其 reference, forName()就是一个办法。它要一个表示 这个类的名字的 String作参数(一定要注意拼写和大小写!)。这个方法 会返回 class的 reference,不过程序里面没用到这个 reference;这 里只是要用它的副作用——看看Gum类装载了没有,要是还没有那就马 上装载。装载的过程中,程序执行了Gum的 static语句。 在上述例程中,如果 Class. forName()没有找到它要装载的类,就会 抛出一个 ClassNotFound Exception(太完美了!单从异常的名字你 就能知道出了什么问题了)。这里,我们只是简单地报告一下问题,然后 继续下去,但是在较为复杂的程序里,你或许应该在异常处理程序里把这 个问题解决掉。 Class常数 Java还提供了一种获取 Class对象的 reference的方法:“cass常数 ( class litera/)”。对于上述程序,它就是 Gum. class 这种写法不但更简单,而且也更安全,因为它是在编译时做检查的。此外 由于没有方法调用,它的执行效率也更高一些。 第5页共17页 www.wgqqh.com/shhgs/tij.html emailshhgsasohu.com

Thinking in Java 3rd Edition 第 5 页 共 17 页 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com "After creating Cookie" }); } } ///:~ Candy,Gum,和 Cookie,每个类都有一条“会在类第一次被装载的 时候执行”的 static 语句。这样装载的类时候就会它就会打印消息通知 你了。对象的创建被分散到了 main( )的各条打印语句之间,其目的就 是要帮助你检测装载的时机。 你可以从程序的输出看到,Class 对象只会在需要的时候装载,而 static 初始化则发生在装载类的时候。 下面这行特别有趣: Class.forName("Gum"); 这是一个 Class 的 static 方法(所有的 Class 对象所共有的)。Class 对象同其它对象一样,也可以用 reference 来操控(这是装载器要干的), 而要想获取其 reference,forName( )就是一个办法。它要一个表示 这个类的名字的 String 作参数(一定要注意拼写和大小写!)。 这个方法 会返回 Class 的 reference,不过程序里面没用到这个 reference;这 里只是要用它的副作用——看看 Gum 类装载了没有,要是还没有那就马 上装载。装载的过程中,程序执行了 Gum 的 static 语句。 在上述例程中,如果 Class.forName( )没有找到它要装载的类,就会 抛出一个 ClassNotFoundException(太完美了!单从异常的名字你 就能知道出了什么问题了)。这里,我们只是简单地报告一下问题,然后 继续下去,但是在较为复杂的程序里,你或许应该在异常处理程序里把这 个问题解决掉。 Class 常数 Java 还提供了一种获取 Class 对象的 reference 的方法:“class 常数 (class literal)”。对于上述程序,它就是: Gum.class; 这种写法不但更简单,而且也更安全,因为它是在编译时做检查的。此外 由于没有方法调用,它的执行效率也更高一些

Chapter 10: Detecting Types Class常数不但能用于普通类,也可以用于接口,数组和 primitive类 型。此外,每种 primitive的 wrapper类还有一个标准的,名为TYPE 的数据成员。这个TYPE能返回“与这种 primitive相关联的 wrapper 类”的cass对象的 reference,就像这样: 等同于 boolean class Boolean TYPE char class Character TYPE byte class Byte TYPE short class ShortTYPE int class Integer TYPE long. class Long TYPE floatclass FloatTYPE double class Double TYPE void. class VoidTYPE 我喜欢尽量使用“, class”,因为这种写法能与普通类的保持一致。 转换之前先作检查 到目前为止,你看到的RTTI的形式有: 1.经典的类型转换;如“( Shape)”,这种转换要经过RTT的检查。要是 做了错误的转换,它就会抛出 Class CastException。 代表对象类型的 Class对象。你可以在运行的时候查询 Class对象,以 此来获取所需的信息。 在C++里,经典的“( Shape)”形式的转换并不动用RTTI。它只是简 单地告诉编译器把这个对象当作那个新的类型来用。但Java会作类型检 查,所以Java的类型转换常常被称作“类型安全的下传”。之所以要说 “下传”,是因为在继承关系图里,派生类一般会放在下面。如果把 Circle到 Shape的转换称作上传,那么 Shape到 Circle的转换就应 该叫下传了。不过你知道 Circle就是 Shape,而且编译器也不会阻止 去上传,但是你不一定知道 Shape是不是一个 Circle,所以如果不进 行明确的类型转换的话,编译器是不会让你把对象赋给派生类的 reference的。 Java里面还有第三种RTTI的形式。这就是 instanceof关键词,它会 告诉你对象是不是某个类的实例。它返回的是一个 boolean值,因此你 就可以用提问的形式来用了,就像这样: if(x instanceof Dog 第6页共17页 www.wgqqh.com/shhgs/tij.html emailshhgsasohu.com

Chapter 10: Detecting Types www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com 第 6 页 共 17 页 Class 常数不但能用于普通类,也可以用于接口,数组和 primitive 类 型。此外,每种 primitive 的 wrapper 类还有一个标准的,名为 TYPE 的数据成员。这个 TYPE 能返回“与这种 primitive 相关联的 wrapper 类”的 Class 对象的 reference,就像这样: ... 等同于... boolean.class Boolean.TYPE char.class Character.TYPE byte.class Byte.TYPE short.class Short.TYPE int.class Integer.TYPE long.class Long.TYPE float.class Float.TYPE double.class Double.TYPE void.class Void.TYPE 我喜欢尽量使用“.class” ,因为这种写法能与普通类的保持一致。 转换之前先作检查 到目前为止,你看到的 RTTI 的形式有: 1. 经典的类型转换;如“(Shape)”,这种转换要经过 RTTI 的检查。要是 做了错误的转换,它就会抛出 ClassCastException。 2. 代表对象类型的 Class 对象。你可以在运行的时候查询 Class 对象,以 此来获取所需的信息。 在 C++里,经典的“(Shape)”形式的转换并不动用 RTTI。它只是简 单地告诉编译器把这个对象当作那个新的类型来用。但 Java 会作类型检 查,所以 Java 的类型转换常常被称作“类型安全的下传”。之所以要说 “下传”,是因为在继承关系图里,派生类一般会放在下面。如果把 Circle 到 Shape 的转换称作上传,那么 Shape 到 Circle 的转换就应 该叫下传了。不过你知道 Circle 就是 Shape,而且编译器也不会阻止 去上传,但是你不一定知道 Shape 是不是一个 Circle,所以如果不进 行明确的类型转换的话,编译器是不会让你把对象赋给派生类的 reference 的。 Java 里面还有第三种 RTTI 的形式。这就是 instanceof 关键词,它会 告诉你对象是不是某个类的实例。它返回的是一个 boolean 值,因此你 就可以用提问的形式来用了,就像这样: if(x instanceof Dog)

Thinking in Java 3 Edition 在将x转换成Dog之前,讦语句会先看看x对象是不是Dog类的。如 果没有其它信息能告诉你这个对象的类型,那么在下传之前先用 instanceof检查一下是很重要的;否则的话,你就很有可能会撞上 Class castException。 通常情况下,你只是要找一种类型(比如把三角形都变成紫色的),但是你 也可以用 instanceof标出所有对象的类型。假设你有一个Pet类系 package cl0 public class pet ()///: v package cl0 lic class Dog extends Pet ()/// //: c10: Pug. java package Cl0; public class Pug extends Dog ()/// package cl0 public class Cat extends Pet ()/// 77: c10: Rodent. java package cl0 public class Rodent extends Pet (/// package cl0; public class Gerbil extends Rodent ()/// package cl0 public class Hamster extends Rodent [1///: 第7页共17页 www.wgqqh.com/shhgs/tij.html

Thinking in Java 3rd Edition 第 7 页 共 17 页 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com ((Dog)x).bark(); 在将 x 转换成 Dog 之前,if 语句会先看看 x 对象是不是 Dog 类的。如 果没有其它信息能告诉你这个对象的类型,那么在下传之前先用 instanceof 检查一下是很重要的;否则的话,你就很有可能会撞上 ClassCastException。 通常情况下,你只是要找一种类型(比如把三角形都变成紫色的),但是你 也可以用 instanceof 标出所有对象的类型。假设你有一个 Pet 类系: //: c10:Pet.java package c10; public class Pet {} ///:~ //: c10:Dog.java package c10; public class Dog extends Pet {} ///:~ //: c10:Pug.java package c10; public class Pug extends Dog {} ///:~ //: c10:Cat.java package c10; public class Cat extends Pet {} ///:~ //: c10:Rodent.java package c10; public class Rodent extends Pet {} ///:~ //: c10:Gerbil.java package c10; public class Gerbil extends Rodent {} ///:~ //: c10:Hamster.java package c10; public class Hamster extends Rodent {} ///:~

Chapter 10: Detecting Types 下面,我们要找出各种Pet的数量,所以我们要用一个带int的类来保 存这个信息。你可以把它当成是一个可以修改的 Integer: //: c10: Counter. java package cl0 public class Counter public String tostring ()(return Integer tostring()i 1 ///: 下一步,我们需要一个能同时持有两种对象的工具:一个表示Pet的类 型,另一个是表示宠物数量的 Counter。也就是说,我们要问“这里有 多少 Gerbil对象?”普通数组作不了这个,因为它是用下标来定位的。 我们要的是能用Pet的类型来定位的数组。我们要将 Counter对象同 Pet对象关联起来。为了能准确的做到这一点,我们会用到一种被称为 “关联性数组( associative array)”的标准数据结构。下面是它最简单 的实现 //: c10: AssociativeArray java Associates keys with values package cl0 import com. bruceeckel. simpletest public class AssociativeArray private static Test monitor new Test( private object[][] pairs private int index public AssociativeArray (int length)I public void put(object key, object value) I if (index > pairs length) airs [index++]= new object[]I key, value )i public object get(object key) t ++) if(key equals(pairs [i][o]) return pairs [i][1] throw new RuntimeException ("Failed to find key )i public string tostring()( String result for (int i =0; i< index; 1++) result + pairs [i][0]+ pairs [i][1 if(i inde It return result 第8页共17页 www.wgqqh.com/shhgs/tij.html

Chapter 10: Detecting Types www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com 第 8 页 共 17 页 下面,我们要找出各种 Pet 的数量,所以我们要用一个带 int 的类来保 存这个信息。你可以把它当成是一个可以修改的 Integer: //: c10:Counter.java package c10; public class Counter { int i; public String toString() { return Integer.toString(i); } } ///:~ 下一步,我们需要一个能同时持有两种对象的工具:一个表示 Pet 的类 型,另一个是表示宠物数量的 Counter。也就是说,我们要问“这里有 多少 Gerbil 对象?”普通数组作不了这个,因为它是用下标来定位的。 我们要的是能用 Pet 的类型来定位的数组。我们要将 Counter 对象同 Pet 对象关联起来。为了能准确的做到这一点,我们会用到一种被称为 “关联性数组(associative array)”的标准数据结构。下面是它最简单 的实现: //: c10:AssociativeArray.java // Associates keys with values. package c10; import com.bruceeckel.simpletest.*; public class AssociativeArray { private static Test monitor = new Test(); private Object[][] pairs; private int index; public AssociativeArray(int length) { pairs = new Object[length][2]; } public void put(Object key, Object value) { if(index >= pairs.length) throw new ArrayIndexOutOfBoundsException(); pairs[index++] = new Object[] { key, value }; } public Object get(Object key) { for(int i = 0; i < index; i++) if(key.equals(pairs[i][0])) return pairs[i][1]; throw new RuntimeException("Failed to find key"); } public String toString() { String result = ""; for(int i = 0; i < index; i++) { result += pairs[i][0] + " : " + pairs[i][1]; if(i < index - 1) result += "\n"; } return result; }

Thinking in java 3 Edition public static void main (String[] args)t AssociativeArray map new AssociativeArray (6) map. put ("sky",blue")i map. put("grass green map. put ("ocean","dancing") map. put ("earth","brown")i map. put("sun",warm") map. put("extra","object")i// Past the end f catch(ArrayIndexOutofBounds Exception e) t System. out. println("Too many objects! )i System. out. println(map)i System. out. println(map. get("ocean")) monitor. expect(new String[] t Too m bjects! sky blue", grass green ocean dancing", tree: tall earth b sun warm' dancing 如果是第一次看到这个程序,你会觉得这好像是一个通用工具,应该把它 放进 com bruceeckel, tools。是的,这的确是一个通用工具—太有 用了,以至于java.util提供了好几种关联性数组(其正式的命名是 Map),它们的功能比这可强多了,速度也快了许多。第11章会用大量 的篇幅讲解关联性数组。但是它们太复杂了,所以这里临时作了一个,目 的就是要让你在理解关联性数组的价值的同时,不至于让问题变得太复 杂 在关联性数组中,下标被称为键(key),而与之相关联的对象则被称为值 (vaUe)。这里,我们用“构建一个双元素数组的pair数组,并且用它 来保存键和值”的方法,建立了键值之间的关联。构造函数创建的是一个 定长数组,因此你得用 index来确保它不会过界。put()一对新的键值 时,它会创建一个新的双元素数组,并且把它放到 pairs的下一个空位 里。如果 index大于等于 pairs的长度,它就抛出异常。 要用get()的时候,你只要把key当参数传给它就可以了,它会帮你找 出值,然后把结果返回给你,要是找不到,它就抛异常。get()用了 种你能想到的最慢的算法:从数组的开头,用 equals()作比较。但是 这里更看重简单而不是效率,而且第11章要将的Map己经解决了这个 问题,因此我们不必担心 第9页共17页 www.wgqqh.com/shhgs/tij.html emailshhgsasohu.com

Thinking in Java 3rd Edition 第 9 页 共 17 页 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com public static void main(String[] args) { AssociativeArray map = new AssociativeArray(6); map.put("sky", "blue"); map.put("grass", "green"); map.put("ocean", "dancing"); map.put("tree", "tall"); map.put("earth", "brown"); map.put("sun", "warm"); try { map.put("extra", "object"); // Past the end } catch(ArrayIndexOutOfBoundsException e) { System.out.println("Too many objects!"); } System.out.println(map); System.out.println(map.get("ocean")); monitor.expect(new String[] { "Too many objects!", "sky : blue", "grass : green", "ocean : dancing", "tree : tall", "earth : brown", "sun : warm", "dancing" }); } } ///:~ 如果是第一次看到这个程序,你会觉得这好像是一个通用工具,应该把它 放进 com.bruceeckel.tools。是的,这的确是一个通用工具——太有 用了,以至于 java.util 提供了好几种关联性数组(其正式的命名是 Map),它们的功能比这可强多了,速度也快了许多。第 11 章会用大量 的篇幅讲解关联性数组。但是它们太复杂了,所以这里临时作了一个,目 的就是要让你在理解关联性数组的价值的同时,不至于让问题变得太复 杂。 在关联性数组中,下标被称为键(key),而与之相关联的对象则被称为值 (value) 。这里,我们用“构建一个双元素数组的 pair 数组,并且用它 来保存键和值”的方法,建立了键值之间的关联。构造函数创建的是一个 定长数组,因此你得用 index 来确保它不会过界。put( )一对新的键值 时,它会创建一个新的双元素数组,并且把它放到 pairs 的下一个空位 里。如果 index 大于等于 pairs 的长度,它就抛出异常。 要用 get( )的时候,你只要把 key 当参数传给它就可以了,它会帮你找 出值,然后把结果返回给你,要是找不到,它就抛异常。get( )用了一 种你能想到的最慢的算法:从数组的开头,用 equals( )作比较。但是 这里更看重简单而不是效率,而且第 11 章要将的 Map 已经解决了这个 问题,因此我们不必担心

Chapter 10: Detecting Types put()和get()是关联性数组的两个基本方法,但是考虑到程序的输 出,我们还覆写了 tostring()方法,让它把键和值全都打印出来。为 了看看它是不是能正常工作,main()先是往 AssociativeArray里填 了一组成对的字符串,然后把它打印了出来,最有再用get()提取了 个值 现在所有工具都已经准备好了,我们可以用 instanceof来数Pet了: //: c10: Petcount java // Using instanceof package cl0; import com. bruceeckel. simpletest* import java.util.*i public class Petcount private static Test monitor private static Random rand neg Random o)i static String[] typenames Rodent","Gerbil","Hamster " / Exceptions thrown to console public static void main(String[ args)i Object[] pets new object[15] Class[] petT Class. forName("c10 Dog Class forName(" c10. Pug") 0. Cat") Class. forName("c10 Rodent ") Class. forName("cl0 Gerbil" ), lass. forName("c10 Hamster") f petTypes [rand. nextInt(petTypes. length)] new Instance( y catch(InstantiationException e) System. out. println("Cannot instantiate")i System. exit(l)i catch(IllegalAccessException e) System. out. println("Cannot access") ystem. exit (1) 1 catch(ClassNot FoundException e)I System. out. println("Cannot find class") AssociativeArray map new AssociativeArray(typenames length for(int map.put(typenames [i], new Counter())i for(int i =0; i< pets. length; i++)i Object if(o instanceof Pet) ((Counter)map. get("Pet")).i++ f if(o instanceof Pug) 第10页共17页 www.wgqqh.com/shhgs/tij.html

Chapter 10: Detecting Types www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com 第 10 页 共 17 页 put( )和 get( )是关联性数组的两个基本方法,但是考虑到程序的输 出,我们还覆写了 toString( )方法,让它把键和值全都打印出来。为 了看看它是不是能正常工作,main( )先是往 AssociativeArray 里填 了一组成对的字符串,然后把它打印了出来,最有再用 get( )提取了一 个值。 现在所有工具都已经准备好了,我们可以用 instanceof 来数 Pet 了: //: c10:PetCount.java // Using instanceof. package c10; import com.bruceeckel.simpletest.*; import java.util.*; public class PetCount { private static Test monitor = new Test(); private static Random rand = new Random(); static String[] typenames = { "Pet", "Dog", "Pug", "Cat", "Rodent", "Gerbil", "Hamster", }; // Exceptions thrown to console: public static void main(String[] args) { Object[] pets = new Object[15]; try { Class[] petTypes = { Class.forName("c10.Dog"), Class.forName("c10.Pug"), Class.forName("c10.Cat"), Class.forName("c10.Rodent"), Class.forName("c10.Gerbil"), Class.forName("c10.Hamster"), }; for(int i = 0; i < pets.length; i++) pets[i] = petTypes[rand.nextInt(petTypes.length)] .newInstance(); } catch(InstantiationException e) { System.out.println("Cannot instantiate"); System.exit(1); } catch(IllegalAccessException e) { System.out.println("Cannot access"); System.exit(1); } catch(ClassNotFoundException e) { System.out.println("Cannot find class"); System.exit(1); } AssociativeArray map = new AssociativeArray(typenames.length); for(int i = 0; i < typenames.length; i++) map.put(typenames[i], new Counter()); for(int i = 0; i < pets.length; i++) { Object o = pets[i]; if(o instanceof Pet) ((Counter)map.get("Pet")).i++; if(o instanceof Dog) ((Counter)map.get("Dog")).i++; if(o instanceof Pug)

点击下载完整版文档(PDF)VIP每日下载上限内不扣除下载券和下载次数;
按次数下载不扣除下载券;
24小时内重复下载只扣除一次;
顺序:VIP每日次数-->可用次数-->下载券;
共25页,试读已结束,阅读完整版请下载
相关文档

关于我们|帮助中心|下载说明|相关软件|意见反馈|联系我们

Copyright © 2008-现在 cucdc.com 高等教育资讯网 版权所有