下载安卓APP箭头
箭头给我发消息

客服QQ:3315713922

Java中的rtti如何执行类型检查

作者:课课家教育     来源: http://www.kokojia.com点击数:936发布时间: 2016-02-21 16:16:59

标签: java的执行类Java的数据成员Java开发

大神带你学编程,欢迎选课

  迄今为止,我们已知的RTTI形式包括:

  (1) 经典造型,如"(Shape)",它用RTTI确保造型的正确性,并在遇到一个失败的造型后产生一个ClassCastException违例。

  (2) 代表对象类型的Class对象。可查询Class对象,获取有用的运行期资料。

Java中的rtti如何执行类型检查_java的执行类_Java的数据成员_课课家

  在C++中,经典的"(Shape)"造型并不执行RTTI。它只是简单地告诉编译器将对象当作新类型处理。而java要执行类型检查,这通常叫作“类型安全”的下溯造型。之所以叫“下溯造型”,是由于类分层结构的历史排列方式造成的。若将一个Circle(圆)造型到一个Shape(几何形状),就叫做上溯造型,因为圆只是几何形状的一个子集。反之,若将Shape造型至Circle,就叫做下溯造型。然而,尽管我们明确知道Circle也是一个Shape,所以编译器能够自动上溯造型,但却不能保证一个Shape肯定是一个Circle。因此,编译器不允许自动下溯造型,除非明确指定一次这样的造型。

  RTTI在Java中存在三种形式。关键字instanceof告诉我们对象是不是一个特定类型的实例(Instance即“实例”)。它会返回一个布尔值,以便以问题的形式使用,就象下面这样:

  if(x instanceof Dog)

  ((Dog)x).bark();

  将x造型至一个Dog前,上面的if语句会检查对象x是否从属于Dog类。进行造型前,如果没有其他信息可以告诉自己对象的类型,那么instanceof的使用是非常重要的——否则会得到一个ClassCastException违例。

  我们最一般的做法是查找一种类型(比如要变成紫色的三角形),但下面这个程序却演示了如何用instanceof标记出所有对象。

  //: PetCount.java

  // Using instanceof

  package c11.petcount;

  import java.util.*;

  class Pet {}

  class Dog extends Pet {}

  class Pug extends Dog {}

  class Cat extends Pet {}

  class Rodent extends Pet {}

  class Gerbil extends Rodent {}

  class Hamster extends Rodent {}

  class Counter { int i; }

  public class PetCount {

  static String[] typenames = {

  "Pet", "Dog", "Pug", "Cat",

  "Rodent", "Gerbil", "Hamster",

  };

  public static void main(String[] args) {

  Vector pets = new Vector();

  try {

  Class[] petTypes = {

  Class.forName("c11.petcount.Dog"),

  Class.forName("c11.petcount.Pug"),

  Class.forName("c11.petcount.Cat"),

  Class.forName("c11.petcount.Rodent"),

  Class.forName("c11.petcount.Gerbil"),

  Class.forName("c11.petcount.Hamster"),

  };

  for(int i = 0; i < 15; i++)

  pets.addElement(

  petTypes[

  (int)(Math.random()*petTypes.length)]

  .newInstance());

  } catch(InstantiationException e) {}

  catch(IllegalAccessException e) {}

  catch(ClassNotFoundException e) {}

  Hashtable h = new Hashtable();

  for(int i = 0; i < typenames.length; i++)

  h.put(typenames[i], new Counter());

  for(int i = 0; i < pets.size(); i++) {

  Object o = pets.elementAt(i);

  if(o instanceof Pet)

  ((Counter)h.get("Pet")).i++;

  if(o instanceof Dog)

  ((Counter)h.get("Dog")).i++;

  if(o instanceof Pug)

  ((Counter)h.get("Pug")).i++;

  if(o instanceof Cat)

  ((Counter)h.get("Cat")).i++;

  if(o instanceof Rodent)

  ((Counter)h.get("Rodent")).i++;

  if(o instanceof Gerbil)

  ((Counter)h.get("Gerbil")).i++;

  if(o instanceof Hamster)

  ((Counter)h.get("Hamster")).i++;

  }

  for(int i = 0; i < pets.size(); i++)

  System.out.println(

  pets.elementAt(i).getClass().toString());

  for(int i = 0; i < typenames.length; i++)

  System.out.println(

  typenames[i] + " quantity: " +

  ((Counter)h.get(typenames[i])).i);

  }

  } ///:~

  在Java 1.0中,对instanceof有一个比较小的限制:只可将其与一个已命名的类型比较,不能同Class对象作对比。在上述例子中,大家可能觉得将所有那些instanceof表达式写出来是件很麻烦的事情。实际情况正是这样。但在Java 1.0中,没有办法让这一工作自动进行——不能创建Class的一个Vector,再将其与之比较。大家最终会意识到,如编写了数量众多的instanceof表达式,整个设计都可能出现问题。

  当然,这个例子只是一个构想——最好在每个类型里添加一个static数据成员,然后在构建器中令其增值,以便跟踪计数。编写程序时,大家可能想象自己拥有类的源码控制权,能够自由改动它。但由于实际情况并非总是这样,所以RTTI显得特别方便。

  1. 使用类标记

  PetCount.java示例可用Java 1.1的类标记重写一遍。得到的结果显得更加明确易懂:

  //: PetCount2.java

  // Using Java 1.1 class literals

  package c11.petcount2;

  import java.util.*;

  class Pet {}

  class Dog extends Pet {}

  class Pug extends Dog {}

  class Cat extends Pet {}

  class Rodent extends Pet {}

  class Gerbil extends Rodent {}

  class Hamster extends Rodent {}

  class Counter { int i; }

  public class PetCount2 {

  public static void main(String[] args) {

  Vector pets = new Vector();

  Class[] petTypes = {

  // Class literals work in Java 1.1+ only:

  Pet.class,

  Dog.class,

  Pug.class,

  Cat.class,

  Rodent.class,

  Gerbil.class,

  Hamster.class,

  };

  try {

  for(int i = 0; i < 15; i++) {

  // Offset by one to eliminate Pet.class:

  int rnd = 1 + (int)(

  Math.random() * (petTypes.length - 1));

  pets.addElement(

  petTypes[rnd].newInstance());

  }

  } catch(InstantiationException e) {}

  catch(IllegalAccessException e) {}

  Hashtable h = new Hashtable();

  for(int i = 0; i < petTypes.length; i++)

  h.put(petTypes[i].toString(),

  new Counter());

  for(int i = 0; i < pets.size(); i++) {

  Object o = pets.elementAt(i);

  if(o instanceof Pet)

  ((Counter)h.get(

  "class c11.petcount2.Pet")).i++;

  if(o instanceof Dog)

  ((Counter)h.get(

  "class c11.petcount2.Dog")).i++;

  if(o instanceof Pug)

  ((Counter)h.get(

  "class c11.petcount2.Pug")).i++;

  if(o instanceof Cat)

  ((Counter)h.get(

  "class c11.petcount2.Cat")).i++;

  if(o instanceof Rodent)

  ((Counter)h.get(

  "class c11.petcount2.Rodent")).i++;

  if(o instanceof Gerbil)

  ((Counter)h.get(

  "class c11.petcount2.Gerbil")).i++;

  if(o instanceof Hamster)

  ((Counter)h.get(

  "class c11.petcount2.Hamster")).i++;

  }

  for(int i = 0; i < pets.size(); i++)

  System.out.println(

  pets.elementAt(i).getClass().toString());

  Enumeration keys = h.keys();

  while(keys.hasMoreElements()) {

  String nm = (String)keys.nextElement();

  Counter cnt = (Counter)h.get(nm);

  System.out.println(

  nm.substring(nm.lastIndexOf('.') + 1) +

  " quantity: " + cnt.i);

  }

  }

  } ///:~

  在这里,typenames(类型名)数组已被删除,改为从Class对象里获取类型名称。注意为此而额外做的工作:例如,类名不是Getbil,而是c11.petcount2.Getbil,其中已包含了包的名字。也要注意系统是能够区分类和接口的。

  也可以看到,petTypes的创建模块不需要用一个try块包围起来,因为它会在编译期得到检查,不会象Class.forName()那样“掷”出任何违例。

  Pet动态创建好以后,可以看到随机数字已得到了限制,位于1和petTypes.length之间,而且不包括零。那是由于零代表的是Pet.class,而且一个普通的Pet对象可能不会有人感兴趣。然而,由于Pet.class是petTypes的一部分,所以所有Pet(宠物)都会算入计数中。

  2. 动态的instanceof

  Java 1.1为Class类添加了isInstance方法。利用它可以动态调用instanceof运算符。而在Java 1.0中,只能静态地调用它(就象前面指出的那样)。因此,所有那些烦人的instanceof语句都可以从PetCount例子中删去了。如下所示:

  //: PetCount3.java

  // Using Java 1.1 isInstance()

  package c11.petcount3;

  import java.util.*;

  class Pet {}

  class Dog extends Pet {}

  class Pug extends Dog {}

  class Cat extends Pet {}

  class Rodent extends Pet {}

  class Gerbil extends Rodent {}

  class Hamster extends Rodent {}

  class Counter { int i; }

  public class PetCount3 {

  public static void main(String[] args) {

  Vector pets = new Vector();

  Class[] petTypes = {

  Pet.class,

  Dog.class,

  Pug.class,

  Cat.class,

  Rodent.class,

  Gerbil.class,

  Hamster.class,

  };

  try {

  for(int i = 0; i < 15; i++) {

  // Offset by one to eliminate Pet.class:

  int rnd = 1 + (int)(

  Math.random() * (petTypes.length - 1));

  pets.addElement(

  petTypes[rnd].newInstance());

  }

  } catch(InstantiationException e) {}

  catch(IllegalAccessException e) {}

  Hashtable h = new Hashtable();

  for(int i = 0; i < petTypes.length; i++)

  h.put(petTypes[i].toString(),

  new Counter());

  for(int i = 0; i < pets.size(); i++) {

  Object o = pets.elementAt(i);

  // Using isInstance to eliminate individual

  // instanceof expressions:

  for (int j = 0; j < petTypes.length; ++j)

  if (petTypes[j].isInstance(o)) {

  String key = petTypes[j].toString();

  ((Counter)h.get(key)).i++;

  }

  }

  for(int i = 0; i < pets.size(); i++)

  System.out.println(

  pets.elementAt(i).getClass().toString());

  Enumeration keys = h.keys();

  while(keys.hasMoreElements()) {

  String nm = (String)keys.nextElement();

  Counter cnt = (Counter)h.get(nm);

  System.out.println(

  nm.substring(nm.lastIndexOf('.') + 1) +

  " quantity: " + cnt.i);

  }

  }

  } ///:~

  可以看到,Java 1.1的isInstance()方法已取消了对instanceof表达式的需要。此外,这也意味着一旦要求添加新类型宠物,只需简单地改变petTypes数组即可;毋需改动程序剩余的部分(但在使用instanceof时却是必需的)。

赞(27)
踩(4)
分享到:
华为认证网络工程师 HCIE直播课视频教程