熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> JSP教程 >> 正文

Java集合的缺點:類型未知

2022-06-13   來源: JSP教程 

  使用Java集合的缺點是在將對象置入一個集合時丟失了類型信息之所以會發生這種情況是由於當初編寫集合時那個集合的程序員根本不知道用戶到底想把什麼類型置入集合若指示某個集合只允許特定的類型會妨礙它成為一個常規用途的工具為用戶帶來麻煩為解決這個問題集合實際容納的是類型為Object的一些對象的句柄這種類型當然代表Java中的所有對象因為它是所有類的根當然也要注意這並不包括基本數據類型因為它們並不是從任何東西繼承來的這是一個很好的方案只是不適用下述場合
  () 將一個對象句柄置入集合時由於類型信息會被拋棄所以任何類型的對象都可進入我們的集合——即便特別指示它只能容納特定類型的對象舉個例子來說雖然指示它只能容納貓但事實上任何人都可以把一條狗扔進來
  () 由於類型信息不復存在所以集合能肯定的唯一事情就是自己容納的是指向一個對象的句柄正式使用它之前必須對其進行造型使其具有正確的類型
  
  值得欣慰的是Java不允許人們濫用置入集合的對象假如將一條狗扔進一個貓的集合那麼仍會將集合內的所有東西都看作貓所以在使用那條狗時會得到一個違例錯誤在同樣的意義上假若試圖將一條狗的句柄造型到一只貓那麼運行期間仍會得到一個違例錯誤
  下面是個例子
  
  //: CatsAndDogsjava
  // Simple collection example (Vector)
  import javautil*;
  
  class Cat {
   private int catNumber;
   Cat(int i) {
    catNumber = i;
   }
   void print() {
    Systemoutprintln(Cat # + catNumber);
   }
  }
  
  class Dog {
   private int dogNumber;
   Dog(int i) {
    dogNumber = i;
   }
   void print() {
    Systemoutprintln(Dog # + dogNumber);
   }
  }
  
  public class CatsAndDogs {
   public static void main(String[] args) {
    Vector cats = new Vector();
    for(int i = ; i < 7; i++)
     cats.addElement(new Cat(i));
    // Not a problem to add a dog to cats:
    cats.addElement(new Dog(7));
    for(int i = 0; i < cats.size(); i++)
     ((Cat)cats.elementAt(i)).print();
    // Dog is detected only at run-time
   }
  } ///:~
  
  可以看出,Vector的使用是非常簡單的:先創建一個,再用addElement()置入對象,以後用elementAt()取得那些對象(注意Vector有一個size()方法,可使我們知道已添加了多少個元素,以便防止誤超邊界,造成違例錯誤)。tw.WInGwiT.COM
  Cat和Dog類都非常淺顯——除了都是“對象”之外,它們並無特別之處(倘若不明確指出從什麼類繼承,就默認為從Object繼承。所以我們不僅能用Vector方法將Cat對象置入這個集合,也能添加Dog對象,同時不會在編譯期和運行期得到任何出錯提示。用Vector方法elementAt()獲取原本認為是Cat的對象時,實際獲得的是指向一個Object的句柄,必須將那個對象造型為Cat。隨後,需要將整個表達式用括號封閉起來,在為Cat調用print()方法之前進行強制造型;否則就會出現一個語法錯誤。在運行期間,如果試圖將Dog對象造型為Cat,就會得到一個違例。
  這些處理的意義都非常深遠。盡管顯得有些麻煩,但卻獲得了安全上的保證。我們從此再難偶然造成一些隱藏得深的錯誤。若程序的一個部分(或幾個部分)將對象插入一個集合,但我們只是通過一次違例在程序的某個部分發現一個錯誤的對象置入了集合,就必須找出插入錯誤的位置。當然,可通過檢查代碼達到這個目的,但這或許是最笨的調試工具。另一方面,我們可從一些標准化的集合類開始自己的編程。盡管它們在功能上存在一些不足,且顯得有些笨拙,但卻能保證沒有隱藏的錯誤。
  
  1. 錯誤有時並不顯露出來
  
  在某些情況下,程序似乎正確地工作,不造型回我們原來的類型。第一種情況是相當特殊的:String類從編譯器獲得了額外的幫助,使其能夠正常工作。只要編譯器期待的是一個String對象,但它沒有得到一個,就會自動調用在Object裡定義、並且能夠由任何Java類覆蓋的toString()方法。這個方法能生成滿足要求的String對象,然後在我們需要的時候使用。
  因此,為了讓自己類的對象能顯示出來,要做的全部事情就是覆蓋toString()方法,如下例所示:
  
  //: WorksAnyway.java
  // In special cases, things just seem
  // to work correctly.
  import java.util.*;
  
  class Mouse {
   private int mouseNumber;
   Mouse(int i) {
    mouseNumber = i;
   }
   // Magic method:
   public String toString() {
    return "This is Mouse #" + mouseNumber;
   }
   void print(String msg) {
    if(msg != null) System.out.println(msg);
    System.out.println(
     "Mouse number " + mouseNumber);
   }
  }
  
  class MouseTrap {
   static void caughtYa(Object m) {
    Mouse mouse = (Mouse)m; // Cast from Object
    mouse.print("Caught one!");
   }
  }
  
  public class WorksAnyway {
   public static void main(String[] args) {
    Vector mice = new Vector();
    for(int i = 0; i < 3; i++)
     mice.addElement(new Mouse(i));
    for(int i = 0; i < mice.size(); i++) {
     // No cast necessary, automatic call
     // to Object.toString():
     System.out.println(
      "Free mouse: " + mice.elementAt(i));
     MouseTrap.caughtYa(mice.elementAt(i));
    }
   }
  } ///:~
  
  可在Mouse裡看到對toString()的重定義代碼。在main()的第二個for循環中,可發現下述語句:
  
  System.out.println("Free mouse: " +
  mice.elementAt(i));
  
  在“+”後,編譯器預期看到的是一個String對象。elementAt()生成了一個Object,所以為獲得希望的String,編譯器會默認調用toString()。但不幸的是,只有針對String才能得到象這樣的結果;其他任何類型都不會進行這樣的轉換。
  隱藏造型的第二種方法已在Mousetrap裡得到了應用。caughtYa()方法接收的不是一個Mouse,而是一個Object。隨後再將其造型為一個Mouse。當然,這樣做是非常冒失的,因為通過接收一個Object,任何東西都可以傳遞給方法。然而,假若造型不正確——如果我們傳遞了錯誤的類型——就會在運行期間得到一個違例錯誤。這當然沒有在編譯期進行檢查好,但仍然能防止問題的發生。注意在使用這個方法時毋需進行造型:
  MouseTrap.caughtYa(mice.elementAt(i));
  
  2. 生成能自動判別類型的Vector
  
  大家或許不想放棄剛才那個問題。一個更“健壯”的方案是用Vector創建一個新類,使其只接收我們指定的類型,也只生成我們希望的類型。如下所示:
  
  //: GopherVector.java
  // A type-conscious Vector
  import java.util.*;
  
  class Gopher {
   private int gopherNumber;
   Gopher(int i) {
    gopherNumber = i;
   }
   void print(String msg) {
    if(msg != null) System.out.println(msg);
    System.out.println(
     "Gopher number " + gopherNumber);
   }
  }
  
  class GopherTrap {
   static void caughtYa(Gopher g) {
    g.print("Caught one!");
   }
  }
  
  class GopherVector {
   private Vector v = new Vector();
   public void addElement(Gopher m) {
    v.addElement(m);
   }
   public Gopher elementAt(int index) {
    return (Gopher)v.elementAt(index);
   }
   public int size() { return v.size(); }
   public static void main(String[] args) {
    GopherVector gophers = new GopherVector();
    for(int i = 0; i < 3; i++)
     gophers.addElement(new Gopher(i));
    for(int i = 0; i < gophers.size(); i++)
     GopherTrap.caughtYa(gophers.elementAt(i));
   }
  } ///:~
  
  這前一個例子類似,只是新的GopherVector類有一個類型為Vector的private成員(從Vector繼承有些麻煩,理由稍後便知),而且方法也和Vector類似。然而,它不會接收和產生普通Object,只對Gopher對象感興趣。
  由於GopherVector只接收一個Gopher(地鼠),所以假如我們使用:
  gophers.addElement(new Pigeon());
  就會在編譯期間獲得一條出錯消息。采用這種方式,盡管從編碼的角度看顯得更令人沉悶,但可以立即判斷出是否使用了正確的類型。
  注意在使用elementAt()時不必進行造型——它肯定是一個Gopher。
  
  3. 參數化類型
  
  這類問題並不是孤立的——我們許多時候都要在其他類型的基礎上創建新類型。此時,在編譯期間擁有特定的類型信息是非常有幫助的。這便是“參數化類型”的概念。在C++中,它由語言通過“模
From:http://tw.wingwit.com/Article/program/Java/JSP/201311/19706.html
  • 上一篇文章:

  • 下一篇文章:
  • 推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.