国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

輕松掌握Java泛型(第1部分)

2019-11-18 11:18:44
字體:
來源:轉載
供稿:網友

  J2SE 1.5 - 代號為 Tiger - 計劃在 2003 年年底發布。我一直都熱衷于盡可能多地收集有關即將推出的新技術的預告信息,因此我將撰寫一系列的文章,討論可從 V1.5 中獲得的新的和經過重組的特性,本文是第一篇。我非凡想談談泛型類型并重點講述在 Tiger 中為了支持它們而進行的更改和調整。
  
  在許多方面,Tiger 肯定是迄今為止在 java 編程方面(包括對源語言語法的重大擴展)所取得的最大進步。Tiger 中計劃進行的最顯著的變化是添加泛型類型,正如在 JSR-14 原型編譯器中所預先展示的那樣(您可以立即免費下載該編譯器;請參閱參考資料)。
  
  讓我們從介紹泛型類型是什么以及添加了什么特性來支持它們開始吧。
  
  數據類型轉換和錯誤
  為理解泛型類型為何如此有用,我們要將注重力轉向 Java 語言中最輕易引發錯誤的因素之一 - 需要不斷地將表達式向下類型轉換(downcast)為比其靜態類型更為具體的數據類型(請參閱參考資料中的“The Double Descent bug pattern”,以了解進行數據類型轉換時,可能會碰到的麻煩的某些方面)。
  
  程序中的每個向下類型轉換對于 ClassCastException 而言都是潛在的危險,應當盡量避免它們。但是在 Java 語言中它們通常是無法避免的,即便在設計優良的程序中也是如此。
  
  在 Java 語言中進行向下類型轉換最常見的原因在于,經常以專用的方式來使用類,這限制了方法調用所返回的參數可能的運行時類型。例如,假定往 Hashtable 中添加元素并從中檢索元素。那么在給定的程序中,被用作鍵的元素類型和存儲在散列表中的值類型,將不能是任意對象。通常,所有的鍵都是某一特定類型的實例。同樣地,存儲的值將共同具有比 Object 更具體的公共類型。
  
  但是在目前現有的 Java 語言版本中,不可能將散列表的特定鍵和元素聲明為比 Object 更具體的類型。在散列表上執行插入和檢索操作的類型特征符告訴我們只能插入和刪除任意對象。例如,put 和 get 操作的說明如下所示:
  
  清單 1. 插入/檢索類型說明表明只能是任意對象
  
  class Hashtable {
   Object put(Object key, Object value) {...}
   Object get(Object key) {...}
   ...
  }
  
  因此,當我們從類 Hashtable 的實例檢索元素時,比如,即使我們知道在 Hashtable 中只放了 String,而類型系統也只知道所檢索的值是 Object 類型。在對檢索到的值進行任何特定于 String 的操作之前,必須將它強制轉換為 String,即使是將檢索到的元素添加到同一代碼塊中,也是如此!
  
  清單 2. 將檢索到的值強制轉換成 String
  
  import java.util.Hashtable;
  class Test {
   public static void main(String[] args) {
    Hashtable h = new Hashtable();
    h.put(new Integer(0), "value");
    String s = (String)h.get(new Integer(0));
    System.out.PRintln(s);
   }
  }
  
  請注重 main 方法主體部分的第三行中需要進行的數據類型轉換。因為 Java 類型系統相當薄弱,因此代碼會因象上面那樣的數據類型轉換而漏洞百出。這些數據類型轉換不僅使 Java 代碼變得更加拖沓冗長,而且它們還降低了靜態類型檢查的價值(因為每個數據類型轉換都是一個選擇忽略靜態類型檢查的偽指令)。我們該如何擴展該類型系統,從而不必回避它呢?
  
  用泛型類型來解決問題!
  要消除如上所述的數據類型轉換,有一種普遍的方法,就是用泛型類型來增大 Java 類型系統。可以將泛型類型看作是類型“函數”;它們通過類型變量進行參數化,這些類型變量可以根據上下文用各種類型參數進行實例化。
  
  例如,與簡單地定義類 Hashtable 不同,我們可以定義泛型類 Hashtable< Key, Value>,其中 Key 和 Value 是類型參數。除了類名后跟著尖括號括起來的一系列類型參數聲明之外,在 Tiger 中定義這樣的泛型類的語法和用于定義普通類的語法很相似。例如,可以按照如下所示的那樣定義自己的泛型 Hashtable 類:
  
  清單 3. 定義泛型 Hashtable 類
  
  class Hashtable< Key, Value> { ... }
  
  然后可以引用這些類型參數,就像我們在類定義主體內引用普通類型那樣,如下所示:
  
  清單 4. 像引用普通類型那樣引用類型參數 class Hashtable< Key, Value> {
   ...
   Value put(Key k, Value v) {...}
   Value get(Key k) {...}
  }
  
  類型參數的作用域就是相應類定義的主體部分(除了靜態成員之外)(在下一篇文章中,我們將討論為何 Tiger 實現中有這樣的“怪習”,即必須對靜態成員進行此項限制。請留意!)。
  
  創建一個新的 Hashtable 實例時,必須傳遞類型參數以指定 Key 和 Value 的類型。傳遞類型參數的方式取決于我們打算如何使用 Hashtable。在上面的示例中,我們真正想要做的是創建 Hashtable 實例,它只將 Integer 映射為 String。可以用新的 Hashtable 類來完成這件事:
  
  清單 5. 創建將 Integer 映射為 String 的實例
  
  import java.util.Hashtable;
  class Test {
   public static void main(String[] args) {
    Hashtable< Integer, String> h = new Hashtable< Integer, String>();
    h.put(new Integer(0), "value");
    ...
   }
  }
  
  現在不再需要數據類型轉換了。請注重用來實例化泛型類 Hashtable 的語法。就像泛型類的類型參數用尖括號括起來那樣,泛型類型應用程序的參數也是用尖括號括起來的。
  
  清單 6. 除去不必要的數據類型轉換
  
  ...
  String s = h.get("key");
  System.out.println(s);
  
  當然,程序員若只是為了能使用泛型類型而必須重新定義所有的標準實用程序類(比如 Hashtable 和 List)的話,則可能會是一項浩大的工程。幸好,Tiger 為用戶提供了所有 Java 集合類的泛型版本,因此我們不必自己動手來重新定義它們了。此外,這些類能與舊代碼和新的泛型代碼一起無縫工作(下個月,我們會說明如何做到這一點)。
  
  Tiger 的基本類型限制
  Tiger 中類型變量的限制之一就是,它們必須用引用類型進行實例化 - 基本類型不起作用。因此,在上面這個示例中,無法完成創建從 int 映射到 String 的 Hashtable。
  
  這很遺憾,因為這意味著只要您想把基本類型用作泛型類型的參數,您就必須把它們組裝為對象。另一方面,當前的這種情況是最糟的;您不能將 int 作為鍵傳遞給 Hashtable,因為所有的鍵都必須是 Object 類型。
  
  我們真正想看到的是,基本類型可以自動進行包裝(boxing)和解包裝(unboxing),類似于用 C# 所進行的操作(或者比后者更好)。遺憾的是,Tiger 不打算包括基本類型的自動包裝(但是人們可以一直期待 Java 1.6 中出現該功能!)。
  
  受限泛型
  有時我們想限制可能出現的泛型類的類型實例化。在上面這個示例中,類 Hashtable 的類型參數可以用我們想用的任何類型參數進行實例化,但是對于其它某些類,我們或許想將可能的類型參數集限定為給定類型范圍內的子類型。
  
  例如,我們可能想定義泛型 ScrollPane 類,它引用普通的帶有滾動條功能的 Pane。被包含的 Pane 的運行時類型通常會是類 Pane 的子類型,但是靜態類型就只是 Pane。
  
  有時我們想用 getter 檢索被包含的 Pane,但是希望 getter 的返回類型盡可能具體些。我們可能想將類型參數 MyPane 添加到 ScrollPane 中,該類型參數可以用 Pane 的任何子類進行實例化。然后可以用這種形式的子句:extends Bound 來說明 MyPane 的聲明,從而來設定 MyPane 的范圍:
  
  清單 7. 用 extends 子句來說明 MyPane 聲明
  
  class ScrollPane< MyPane extends Pane> { ... }
  
  當然,我們可以完全不使用顯式的范圍,只要能確保沒有用不適當的類型來實例化類型參數。
  
  為什么要自找麻煩在類型參數上設定范圍呢?這里有兩個原因。首先,范圍使我們增加了靜態類型檢查功能。有了靜態類型檢查,就能保證泛型類型的每次實例化都符合所設定的范圍。
  
  其次,因為我們知道類型參數的每次實例化都是這個范圍之內的子類,所以可以放心地調用類型參數實例出現在這個范圍之內的任何方法。假如沒有對參數設定顯式的范圍,那么缺省情況下范圍是 Object,這意味著我們不能調用范圍實例在 Object 中未曾出現的任何方法。
  
  多態方法
  除了用類型參數對類進行參數化之外,用類型參數對方法進行參數化往往也同樣很有用。泛型 Java 編程用語中,用類型進行參數化的方法被稱為多態方法(Polymorphic method)。
  
  多態方法之所以有用,是因為有時候,在一些我們想執行的操作中,參數與返回值之間的類型相關性原本就是泛型的,但是這個泛型性質不依靠于任何類級的類型信息,而且對于各個方法調用都不相同。
  
  例如,假定想將 factory 方法添加到 List 類中。這個靜態方法只帶一個參數,也將是 List 唯一的元素(直到添加了其它元素)。因為我們希望 List 成為其所包含的元素類型的泛型,所以希望靜態 factory 方法帶有類型變量 T 這一參數并返回 List< T> 的實例。
  
  但是我們確實希望該類型變量 T 能在方法級別上進行聲明,因為它會隨每次單獨的方法調用而發生改變(而且,正如我在下一篇文章中將討論的那樣,Tiger 設計的“怪習”規定靜態成員不在類級類型參數的范疇之內)

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 依安县| 习水县| 胶州市| 宝清县| 盐边县| 文昌市| 来凤县| 安仁县| 银川市| 普陀区| 新晃| 资源县| 若羌县| 文水县| 吉木萨尔县| 大庆市| 高台县| 中超| 锦州市| 饶河县| 扬中市| 泉州市| 清苑县| 武功县| 小金县| 岳西县| 手游| 通城县| 界首市| 邛崃市| 西丰县| 洪湖市| 澄迈县| 贵南县| 根河市| 三河市| 泰和县| 陕西省| 黎平县| 余姚市| 都昌县|