泛型(Generics)簡介 J
SE
中的最顯著的變化之一是添加對泛型類型的支持
在J
SE
以及之前的版本中
Java程序並不是類型安全的
例如
Collection framework中定義的List
Map等容器類的元素都是Object類型
即這個類包含的元素是Object對象
使用這種方式實現的列表
可以用來操作整數
實數
字符串或者任何對象類型
例如
清單
類型不安全的代碼示例
List stringList = new ArrayList();
stringList
add(
abcde
);
String str = (String)stringList
get(
);
這種方法實現的列表需要使用強制類型轉換(又稱顯示造型)
因此不是類型安全的
在上面這段代碼種
雖然變量名為stringList
但是我們仍然可以把一個整型對象添加到這個隊列中
例如
清單
類型不安全的代碼示例
stringList
add(new Integer(
));
在這種情況下
從字符列表中獲取對象時
強制類型轉換就會導致運行時異常
清單
類型不安全的代碼示例
String str = (String)stringList
get(
); //runtime exception
泛型是Java邁向類型安全的一個重要步驟
使用泛型可以構造出類型安全的代碼
聲明泛型 所謂泛型是指類型參數化(parameterized types)
Java是一種強類型的語言
在J
SE
以及以前的版本中
我們在定義一個Java類
接口或者方法的時候
必須指定變量的類型
在聲明泛型類
接口或者函數時
定義變量的時候不指定某些變量的具體類型
而是用一個類型參數代替
在使用這個類
接口
或者方法的時候
這個類型參數由一個具體類型所代替
泛型類
下面的例子中介紹了如何創建一個最簡單泛型類
清單
最簡單的泛型類
public class GenSample<T> {}
類名後面帶有<T>表明了這個類是泛型類
其中T被成為類型參數(type parameter)
在使用泛型的時候
類型參數可以被替換為任何的類類型
但是不能是原始類型(primitive type)
例如int
double
下面通過一個列表的例子來具體說明如果聲明泛型類和類型參數的用法
清單
泛型列表
public class GenList <T>{
private T[] elements;
private int size =
;
private int length =
;
public GenList(int size) {
elements = (T[])new Object[size];
this
size = size;
}
public T get(int i) {
if (i < length) {
return elements[i];
}
return null;
}
public void add(T e) {
if (length < size
)
elements[length++] = e;
}
}
在列表的例子中
類型參數T被用來表示列表中的元素的類型
即
這個列表中的元素是T類型的
在使用這個列表時
這個類型參數T會被具體的類型所替代
注意
由於T時類型參數不是具體的類
所以不能使用new操作符創建T的對象
例如new T()
或者
new T[
]
泛型接口
在J
SE
中
不僅僅可以聲明泛型類
也可以聲明泛型接口
聲明泛型接口和聲明泛型類的語法類似
也是在接口命稱後面加上<T>
例如
清單
泛型接口
public interface GenInterface<T> {
void func(T t);
}
聲明多個類型參數的泛型類或者接口
在聲明泛型類的時候
可是使用多個類型參數
多個類型參數之間用逗號分開
例如
清單
多個類型參數的泛型類
public class GenMap<T
V> {}
Eclipse
的類創建向導支持創建泛型類
如下圖所示
圖
使用類向導創建泛型類
educitycn/img_///jpg > 使用Eclipse接口向導創建泛型接口
圖
使用接口向導創建泛型接口
educitycn/img_///jpg > 泛型方法
類型參數(type parameter)不僅僅可以用來聲明泛型類或者泛型接口
也可以用來聲明泛型方法
而且這種聲明的泛型方法可以用在非泛型類中
聲明泛型方法的一般格式是
清單
泛型方法的一般形式
<type
list> return
type method
name(parameter
list) {}
清單
泛型方法示例
public <T> String getString(T obj) {
return obj
toString();
}
受限泛型
受限泛型是指類型參數的取值范圍是受到限制的
extends關鍵字不僅僅可以用來聲明類的繼承關系
也可以用來聲明類型參數(type parameter)的受限關系
例如
我們只需要一個存放數字的列表
包括整數(Long
Integer
Short)
實數(Double
Float)
不能用來存放其他類型
例如字符串(String)
也就是說
要把類型參數T的取值泛型限制在Number極其子類中
在這種情況下
我們就可以使用extends關鍵字把類型參數(type parameter)限制為數字
清單
受限泛型示例
public class Limited<T extends Number> {
public static void main(String[] args) {
Limited<Integer> number; //正確
Limited<String> str; //編譯錯誤
}
}
在Eclipse
中
上例中的編譯錯誤信息如下圖所示
圖
由於受限泛型導致的編譯錯誤
educitycn/img_///jpg > 在程序中使用泛型 在程序中使用泛型類
在創建泛型類的對象的時候
和創建普通對象基本類似
必須提供具體的類類型來替代類型參數T (J
SE
目前不支持原始類型作為類型參數(type parameter))
清單
使用泛型類
//如果需要整型的列表
GenList<Integer> integerList = new GenList<Integer>();
//如果需要字符型的列表
GenList<String> strList = new GenList<String>();
//不能使用原始類型
GenList<int> nList = new GenList<int>(); //編譯錯誤
使用泛型解決類型安全性問題
使用泛型實現的列表是類型安全的
下列破壞類型安全語句會在編譯的時候檢查出來
把鼠標放在錯誤標記上
Eclipse
中的錯誤提示就會顯示
如下圖所示
圖
破壞類型安全引起的錯誤
educitycn/img_///jpg > 二義性錯誤
GenMap在聲明是使用了
個類型參數T和V
因此在創建GenMap的對象的時候也需要提供
個具體的類類型來替代這
個類型參數
例如
清單
多個參數的泛型類
GenMap<Integer
String> gm = new GenMap<Integer
String>();
GenMap<String
String> gm
= new GenMap<String
String>();
上例中
T和V雖然看起來是兩個不同的類型參數
但是在使用這個泛型類的時候
T和V很有可能被替換成同一種類型
因此在聲明多個類型參數的泛型類時
要注意避免這種二義性錯誤
例如
清單
二義性錯誤
public class GenMap<T
V> {
//編譯錯誤
二義性錯誤
public void set(T t){}
public void set(V v){}
}
在上面這段代碼
如果T和V被替換成同一種類型
set函數的簽名(signature)就是完全一樣的
所以編譯器會報告二義性錯誤
正確的用法是聲明
個不同名的方法
例如
清單
二義性錯誤
public class GenMap<T
V> {
public void setKey(T t){}
public void setValue(V v){}
}
圖
二義性錯誤
educitycn/img_///jpg > 使用通配符
前面我們創建了泛型的列表
如果我需要一個方法來處理泛型列表
例如
我們希望把列表中的每個元素都打印出來
但是類型參數(type parameter)只能使用在聲明一個泛型類的時候
如果類型參數使用在函數定義裡會導致編譯錯誤
public static void print(GenList<T> list){} //編譯錯誤
在這種情況下
我們需要用另外一種方法來表示一個泛型類
否則
就可能需要書寫多個print函數
public static void print(GenList<Integer> list){}
public static void print(GenList<Double> list){}
…
public static void print(GenList<String> list){}
J
SE
中提供了泛型的通配符
?
?
可以用來代替任何類型
例如使用通配符來實現print方法
public static void print(GenList<?> list) {}
泛型的一些局限型 (
) 類型參數不能實例化
例如
T t= new T(); //編譯錯誤
(
) 不能實例化類型參數的數組
T[] ts= new T[
]; //編譯錯誤
(
) 類的靜態變量不能聲明為類型參數類型
public
From:http://tw.wingwit.com/Article/program/Java/ky/201311/28091.html