您好,登錄后才能下訂單哦!
這篇文章主要介紹“Java泛型該怎么掌握”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“Java泛型該怎么掌握”文章能幫助大家解決問題。
數據類型轉換和錯誤
為理解泛型類型為何如此有用,我們要將注意力轉向 Java 語言中最容易引發錯誤的因素之一 - 需要不斷地將表達式向下類型轉換(downcast)為比其靜態類型更為具體的數據類型。
程序中的每個向下類型轉換對于 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
是類型參數。除了類名后跟著尖括號括起來的一系列類型參數聲明之外,在 Tiger 中定義這樣的泛型類的語法和用于定義普通類的語法很相似。例如,可以按照如下所示的那樣定義自己的泛型 Hashtable
類:
清單 3. 定義泛型 Hashtable 類
class Hashtable{ ... } |
然后可以引用這些類型參數,就像我們在類定義主體內引用普通類型那樣,如下所示:
清單 4. 像引用普通類型那樣引用類型參數
class Hashtable{ ... 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) { Hashtableh = new Hashtable(); 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{ ... } |
當然,我們可以完全不使用顯式的范圍,只要能確保沒有用不適當的類型來實例化類型參數。
為什么要自找麻煩在類型參數上設定范圍呢?這里有兩個原因。首先,范圍使我們增加了靜態類型檢查功能。有了靜態類型檢查,就能保證泛型類型的每次實例化都符合所設定的范圍。
其次,因為我們知道類型參數的每次實例化都是這個范圍之內的子類,所以可以放心地調用類型參數實例出現在這個范圍之內的任何方法。如果沒有對參數設定顯式的范圍,那么缺省情況下范圍是 Object
,這意味著我們不能調用范圍實例在 Object
中未曾出現的任何方法。
多態方法
除了用類型參數對類進行參數化之外,用類型參數對方法進行參數化往往也同樣很有用。泛型 Java 編程用語中,用類型進行參數化的方法被稱為 多態方法(Polymorphic method)。
多態方法之所以有用,是因為有時候,在一些我們想執行的操作中,參數與返回值之間的類型相關性原本就是泛型的,但是這個泛型性質不依賴于任何類級的類型信息,而且對于各個方法調用都不相同。
例如,假定想將 factory
方法添加到 List
類中。這個靜態方法只帶一個參數,也將是 List 唯一的元素(直到添加了其它元素)。因為我們希望 List
成為其所包含的元素類型的泛型,所以希望靜態 factory
方法帶有類型變量 T
這一參數并返回 List
的實例。
但是我們確實希望該類型變量 T
能在方法級別上進行聲明,因為它會隨每次單獨的方法調用而發生改變(而且,正如我在下一篇文章中將討論的那樣,Tiger 設計的“怪習”規定靜態成員不在類級類型參數的范疇之內)。Tiger 讓我們通過將類型參數作為方法聲明的前綴,從而在單獨的方法級別上聲明類型參數。例如,可以按照如下所示的那樣為 factory
方法 make
添加前綴:
清單 8. 將類型參數作為前綴添加到方法聲明
class Utilities {public static Listmake(T first) { return new List(first); } } |
除了多態方法中所增加的靈活性之外,Tiger 中還增加了一個優點。Tiger 使用類型推斷機制,根據參數類型來自動推斷出多態方法的類型。這可以大大減少方法調用的繁瑣和復雜性。例如,如果想調用 make
方法來構造包含 new Integer(0)
的 List
新實例,那么只需編寫:
清單 9. 強制 make 構造新實例
Utilities.make(Integer(0)) |
然后會自動地從方法參數中推斷出類型參數的實例化。
關于“Java泛型該怎么掌握”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。