程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> rtti在java造型前的檢查

rtti在java造型前的檢查

編輯:關於JAVA

迄今為止,我們已知的RTTI形式包括:
(1) 經典造型,如"(Shape)",它用RTTI確保造型的正確性,並在遇到一個失敗的造型後產生一個ClassCastException違例。
(2) 代表對象類型的Class對象。可查詢Class對象,獲取有用的運行期資料。

在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時卻是必需的)。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved