J
SE
- 代號為 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 java
util
Hashtable;
class Test {
public static void main(String[] args) {
Hashtable h = new Hashtable();
h
put(new Integer(
)
value
);
String s = (String)h
get(new Integer(
));
System
out
println(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 java
util
Hashtable;
class Test {
public static void main(String[] args) {
Hashtable< Integer, String> h = new Hashtable< Integer, String>();
h
put(new Integer(
)
value
);
}
}
現在不再需要數據類型轉換了
請注意用來實例化泛型類 Hashtable 的語法
就像泛型類的類型參數用尖括號括起來那樣
泛型類型應用程序的參數也是用尖括號括起來的
清單
除去不必要的數據類型轉換
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
中出現該功能!)
受限泛型 有時我們想限制可能出現的泛型類的類型實例化
在上面這個示例中
類 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