java基礎-中級(二)【泛型】
目錄
2.6 Class<T>
2、泛型
Java 泛型(generics)是 JDK 5 中引入的一個新特性, 泛型提供了編譯時型別安全檢測機制,該機制允許程式設計師在編譯時檢測到非法的型別。泛型的本質是引數化型別,也就是說所操作的資料型別被指定為一個引數。
這種引數型別可以用在類、介面和方法的建立中,分別稱為泛型類、泛型介面、泛型方法。
泛型的用法:
泛型可以用以下幾種形式:
- 用在類上 class Test<E>
- 用在方法中 <U> returnType m( U o)
2.1 泛型方法
定義泛型方法的規則
- 所有泛型方法宣告都有一個型別引數宣告部分(由尖括號分隔),該型別引數宣告部分在方法返回型別之前(在下面例子中的<E>)。
- 每一個型別引數宣告部分包含一個或多個型別引數,引數間用逗號隔開。一個泛型引數,也被稱為一個型別變數,是用於指定一個泛型型別名稱的識別符號。
- 型別引數能被用來宣告返回值型別,並且能作為泛型方法得到的實際引數型別的佔位符。
- 泛型方法體的宣告和其他方法一樣。注意型別引數只能代表引用型型別,不能是原始型別(像int,double,char的等)。
泛型方法的例子:
public class GenericMethodTest { // 泛型方法 printArray public static < E > void printArray( E[] inputArray ) { // 輸出陣列元素 for ( E element : inputArray ){ System.out.printf( "%s ", element ); } System.out.println(); } public static void main( String args[] ) { // 建立不同型別陣列: Integer, Double 和 Character Integer[] intArray = { 1, 2, 3, 4, 5 }; Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 }; Character[] charArray = { 'H', 'E', 'L', 'L', 'O' }; System.out.println( "整型陣列元素為:" ); printArray( intArray ); // 傳遞一個整型陣列 System.out.println( "\n雙精度型陣列元素為:" ); printArray( doubleArray ); // 傳遞一個雙精度型陣列 System.out.println( "\n字元型陣列元素為:" ); printArray( charArray ); // 傳遞一個字元型陣列 } }
型別變數的限定
有時,類或方法要對型別變數加以約束。看下面一個例子:
//下面是比較最小值的方法
public static <T> T min(T[] a){
if(a==null||a.length==0) return null;
T small =a[0];
for(T t:a){
if(small.compareTo(t)>0) small=t;
}
return small;
}
但是上面的方法是不對的,因為並沒有指定型別變數T所屬的類就有CompareTo方法,所以要將型別變數T的限定實現Comparable,下面的方法才是正確的
public static <T extends Comparable> T min(T[] a){
if(a==null||a.length==0) return null;
T small =a[0];
for(T t:a){
if(small.compareTo(t)>0) small=t;
}
return small;
}
2.2 泛型類
泛型類的宣告和非泛型類的宣告類似,除了在類名後面添加了型別引數宣告部分。
和泛型方法一樣,泛型類的型別引數宣告部分也包含一個或多個型別引數,引數間用逗號隔開。一個泛型引數,也被稱為一個型別變數,是用於指定一個泛型型別名稱的識別符號。
下面使用了兩個泛型的類:在定義類時,不能確定引數的型別或者類
import java.util.ArrayList;
public class Model <E,T> {
private E e;
private T t;
public E getE() {
return e;
}
public void setE(E e) {
this.e = e;
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
public static void main(String[] args) {
Model<String,Object> model = new Model<String, Object>();
model.setE("mm");
model.setT(new ArrayList<String>());
}
}
2.3 泛型介面
泛型介面的定義格式:
interface 介面名<宣告自定義泛型>{}
泛型介面要注意的事項
- 介面上自定義的泛型的具體資料型別是在實現一個介面的時候指定的。
- 在介面上自定義的泛型如果在實現介面的時候沒有指定具體的資料型別,那麼預設為Object型別
- 目前實現一個介面的時候,還不明確目前要操作的資料型別,要等到建立介面實現類物件的時候才去指定泛型的具體資料型別如果要延長介面自定義泛型 的具體資料型別,那麼格式如下:
修飾符 class 類名<宣告自定義泛型> implements 介面名<宣告自定義泛型>{}
2.4 泛型的約束與侷限
下面講解使用Java泛型時需要考慮的一些限制,大多數限制都是由於型別擦除引起的。
型別擦除:
Java泛型(Generic)的引入加強了引數型別的安全性,減少了型別的轉換,但有一點需要注意:
Java 的泛型在編譯器有效,在執行期被刪除,也就是說所有泛型引數型別在編譯後都會被清除掉,因為虛擬機器不支援泛型,將型別變數換成限定型別(無限定變數轉換成Object)
2.4.1 不能用基本型別例項化型別引數
不能用型別引數代替基本型別,因此沒有Pair<double>,只有Pair<Double>.原因就是型別擦除後Pair類中含有Object的域,而Object不能儲存double。
2.4.2 執行時型別查詢只適用於原始型別
所有型別的查詢只是與原始型別進行對比,不能加入型別變數。
if(a instanceOf Pair<String>)//錯誤使用
if(a instanceOf Pair)//正確寫法
同理,getClass方法也只是返回原始型別:
Pair<String> strPair=..;
Pair<Employee> empPair=..;
if(strPair.getClass()==empPair.getClass())//結果true
2.4.3 不能建立引數化型別的陣列
Pair<String>[] table = new Pair<String>[10];//錯誤寫法
型別擦出後,變成了table的型別為Pair[],可以將其轉換成Object[]
Pair<String>[] table = new Pair<String>[10]
Object[] obj = table;
obj[0] = "hello";//編譯丟擲異常
綜上:可以定義 Pair<String>[] table,但是不要使用new Pair<String>[10]初始化變數。
2.4.4 不能例項化型別變數
不能使用new T(),new T[],T.class這樣的表示式的型別變數。
2.4.5 泛型類的靜態上下文中型別變數無效
不能在靜態域或者方法中引用型別變數。
public static T getInstance(){}//錯誤寫法
2.4.6 不能丟擲或捕捉泛型類的例項
既不能丟擲也不能捕捉泛型類物件。但是在處理異常的方法中可以使用型別變數。
try{
}catch(Exception e){
t.methods();
}
2.5 泛型型別繼承規則
使用泛型類時,我們有必要了解一些泛型類之間有關繼承和子型別的準則。相同的類中包含不同的泛型,就代表著沒有什麼聯絡,例如MallSK<T>和MasllSK<S>,這個T和S之間即便有繼承關係,這兩個類的物件也不能相互轉換。
泛型與陣列不同,陣列中將一個子類的陣列存放到一個父類的陣列中,也就是Manager[]陣列賦值給Employee[]陣列,當Manager陣列的存放值改變時,如果將一個低級別的僱員放在高級別的僱員位置,虛擬機器會丟擲ArrayStoryException異常。
2.6 Class<T>
class<T>中的T代表Class類的型別。例如,String.class
的型別是 Class<String>
。使用Class<T>作為返回值的好處是可以確定返回型別,而不是使用Object為返回型別,使用Object作為返回型別的缺點是容易將類的型別轉錯。