編輯按
《Java in a Nutshell
th Edition》覆蓋了jdk
中很多變化和新特征
其中最重要的就是泛型
在本文的第一部分
作者David Flanagan介紹了如何使用泛型
而在第二部分
作者描述了如何寫你自己的泛型和泛型方法
Java
的新特性之一是引入了泛型類型和泛型方法
一個泛型類型通過使用一個或多個類型變量來定義
並擁有一個或多個使用一個類型變量作為一個參數或者返回值的占位符
例如
類型java
util
List<E>是一個泛型類型
一個list
其元素的類型被占位符E描述
這個類型有一個名為add()的方法
被聲明為有一個類型為E的參數
同時
有一個get()方法
返回值被聲明為E類型
為了使用泛型類型
你應該為類型變量詳細指明實際的類型
形成一個就像List<String>類似的參數化類型
[
]指明這些額外的類型信息的原因是編譯器據此能夠在編譯期為您提供很強的類型檢查
增強您的程序的類型安全性
舉個例子來說
您有一個只能保持String對象的List
那麼這種類型檢查就能夠阻止您往裡面加入String[]對象
同樣的
增加的類型信息使編譯器能夠為您做一些類型轉換的事情
比如
編譯器知道了一個List<String>有個get()方法
其返回值是一個String對象
因此您不再需要去將返回值由一個Object強制轉換為String
Java
util包中的集合類在java
中已經被做成了泛型
也許您將會在您的程序中頻繁的使用到他們
類型安全的集合類就是一個泛型類型的典型案例
即便您從沒有定義過您自己的泛型類型甚至從未用過除了java
util中的集合類以外的泛型類型
類型安全的集合類的好處也是極有意義的一個標志——他們證明了這個主要的新語言特性的復雜性
我們從探索類型安全的集合類中的基本的泛型用法開始
進而研究更多使用泛型類型的復雜細節
然後我們討論類型參數通配符和有界通配符
描繪了如何使用泛型以後
我們闡明如何編寫自己的泛型類型和泛型方法
我們對於泛型的討論將結束於一趟對於JavaAPI的核心中重要的泛型類型的旅行
這趟旅程將探索這些類型以及他們的用法
旅程的目的是為了讓您對泛型如何工作這個問題有個深入的理解
類型安全集合類 Java
util類包包含了Java集合框架(Java Collections Framework)
這是一批包含對象的set
對象的list以及基於key
value的map
第五章將談到集合類
這裡
我們討論的是在java
中集合類使用類型參數來界定集合中的對象的類型
這個討論並不適合java
或更早期版本
如果沒有泛型
對於集合類的使用需要程序員記住每個集合中元素的類型
當您在java
種創建了一個集合
您知道您放入到集合中的對象的類型
但是編譯器不知道
您必須小心地往其中加入一個合適類型的元素
當需要從集合中獲取元素時
您必須顯式的寫強制類型轉換以將他們從Object轉換為他們真是的類型
考察下邊的java
的代碼
public static void main(String[] args) { // This list is intended to hold only strings
// The compiler doesn
t know that so we have to remember ourselves
List wordlist = new ArrayList(); // Oops! We added a String[] instead of a String
// The compiler doesn
t know that this is an error
wordlist
add(args); // Since List can hold arbitrary objects
the get() method returns // Object
Since the list is intended to hold strings
we cast the // return value to String but get a ClassCastException because of // the error above
String word = (String)wordlist
get(
);}
泛型類型解決了這段代碼中的顯示的類型安全問題
Java
util中的List或是其他集合類已經使用泛型重寫過了
就像前面提到的
List被重新定義為一個list
它中間的元素類型被一個類型可變的名稱為E的占位符描述
Add()方法被重新定義為期望一個類型為E的參數
用於替換以前的Object
get()方法被重新定義為返回一個E
替換了以前的Object
在java
中
當我們申明一個List或者創建一個ArrayList的實例的時候
我們需要在泛型類型的名字後面緊跟一對
<>
尖括號中寫入我們需要的實際的類型
比如
一個保持String的List應該寫成
List<String>
需要注意的是
這非常象給一個方法傳一個參數
區別是我們使用類型而不是值
同時使用尖括號而不是圓括號
Java
util的集合類中的元素必須是對象化的
他們不能是基本類型
泛型的引入並沒有改變這點
泛型不能使用基本類型
我們不能這樣來申明——Set<char>或者List<int>
記住
無論如何
java
中的自動打包和自動解包特性使得使用Set<Character>或者List<Integer>和直接使用char和int值一樣方便
(查看第二章以了解更多關於自動打包和自動解包的細節)
在Java中上面的例子將被重寫為如下方式 public static void main(String[] args) { // This list can only hold String objects List<String> wordlist = new ArrayList<String>(); // args is a String[]
not String
so the compiler won
t let us do this wordlist
add(args); // Compilation error! // We can do this
though
// Notice the use of the new for/in looping statement for(String arg : args) wordlist
add(arg); // No cast is required
List<String>
get() returns a String
String word = wordlist
get(
);}
值得注意的是代碼量其實並沒有比原來那個沒有泛型的例子少多少
使用
(String)
這樣的類型轉換被替換成了類型參數
<String>
不同的是類型參數需要且僅需要聲明一次
而list能夠被使用任何多次
不需要類型轉換
在更長點的例子代碼中
這一點將更加明顯
即使在那些看上去泛型語法比非泛型語法要冗長的例子裡
使用泛型依然是非常有價值的——額外的類型信息允許編譯器在您的代碼裡執行更強的錯誤檢查
以前只能在運行起才能發現的錯誤現在能夠在編譯時就被發現
此外
以前為了處理類型轉換的異常
我們需要添加額外的代碼行
如果沒有泛型
那麼當發生類型轉換異常的時候
一個ClassCastException異常就會被從實際代碼中拋出
就像一個方法可以使用任意數量的參數一樣
類允許使用多個類型變量
接口Java
util
Map就是一個例子
一個Map體現了從一個key的對象到一個value的對象的映射關系
接口Map申明了一個類型變量來描述key的類型而另一個類型變量來描述value的類型
舉個例子來說
假設您希望做一個String對象到Integer對象的映射關系
public static void main(String[] args) { // A map from strings to their position in the args[] array Map<String
Integer> map = new HashMap<String
Integer>(); // Note that we use autoboxing to wrap i in an Integer object
for(int i=
; i < args
length; i++) map
put(args[i]
i); // Find the array index of a word
Note no cast is required! Integer position = map
get(
hello
); // We can also rely on autounboxing to convert directly to an int
// but this throws a NullPointerException if the key does not exist // in the map int pos = map
get(
world
);}
象List<String>這個一個參數類型其本身也是也一個類型
也能夠被用於當作其他類型的一個類型變量值
您可能會看到這樣的代碼
// Look at all those nested angle brackets!Map<String
List<List<int[]>>> map = getWeirdMap();// The compiler knows all the types and we can write expressions// like this without casting
We might still get NullPointerException// or ArrayIndexOutOfBounds at runtime
of course
int value = map
get(key)
get(
)
get(
)[
];// Here
s how we break that expression down step by step
List<List<int[]>> listOfLists = map
get(key);List<int[]> listOfIntArrays = listOfLists
get(
);int[] array = listOfIntArrays
get(
);int element = array[
];
在上面的代碼裡
java
util
List<E>和java
util
Map<K
V>的get()方法返回一個類型為E的list元素或者一個類型為V的map元素
注意
無論如何
泛型類型能夠更精密的使用他們的變量
在本書中的參考章節查看List<E>
您將會看到它的iterator( )方法被聲明為返回一個Iterator<E>
這意味著
這個方法返回一個跟list的實際的參數類型一樣的一個參數類型的實例
為了具體的說明這點
下面的例子提供了不使用get(
)方法來獲取一個List<String>的第一個元素的方法
List<String> words = //
initialized elsewhere
Iterator<String> iterator = erator();String firstword = iterator
next();
理解泛型類型 本段將對泛型類型的使用細節做進一步的探討
以嘗試說明下列問題
不帶類型參數的使用泛型的後果
參數化類型的體系
一個關於編譯期泛型類型的類型安全的漏洞和一個用於確保運行期類型安全的補丁
為什麼參數化類型的數組不是類型安全的
未經處理的類型和不被檢查的警告
即使被重寫的Java集合類帶來了泛型的好處
在使用他們的時候您也不被要求說明類型變量
一個不帶類型變量的泛型類型被認為是一個未經處理的類型(raw type)
這樣
版本以前的jav
From:http://tw.wingwit.com/Article/program/Java/hx/201311/26671.html