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

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

2013-11-15 11:51:46  來源: JSP教程 

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