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 已经解决了这个 问题,因此我们不必担心