天天看點

Java随筆—泛型程式設計引言1.為什麼使用泛型?2.定義簡單泛型類3.泛型方法4.類型變量的限定5.限制與局限性6.泛型類型的繼承規則7.通配符

目錄

  • 引言
  • 1.為什麼使用泛型?
  • 2.定義簡單泛型類
  • 3.泛型方法
  • 4.類型變量的限定
  • 5.限制與局限性
  • 6.泛型類型的繼承規則
  • 7.通配符

引言

泛型類似于一個模闆,實質就是參數化類型,通過一個類型參數T,用來訓示元素的類型,可以被用在類、接口和方法中,對應的稱為泛型類、泛型接口、泛型方法。

1.為什麼使用泛型?

Java還沒加入泛型類前,泛型程式設計實際是通過繼承實作的。AllayList隻維護一個Object引用的數組

public class ArrayList  {
  private Object [] arr;
  public ObjArray(int n) {
    this.arr = new Object[n];
  }
  public void add (int i, Object o) {
    this.arr[i] = o;
  }
  public Object get (int i) {
    return this.arr[i];
  }
}
           

這樣設計有兩個問題:

當擷取一個值時,必須進行強制類型轉換;

可以向數組清單中加入任何類型的對象,但是編譯和運作不報錯;

為此引入泛型機制,在調用get時,不需要進行強制類型轉化,編譯器知道傳回值類型,并且可以避免插入錯誤類型,類型參數T使得程式具有更好的安全性和可讀性。

2.定義簡單泛型類

一個泛型類可以有一個或者多個類型變量的類

public class Pair<T>{...}
public class Pair<T,U>{...}
           

類型變量使用大寫,Java中,一般使用變量E代表集合的元素類型,K表示關鍵字,V表示關鍵字值的類型,T表示任意類型。

用具體的類型替代類型變量就可以執行個體化泛型類型:Pair&ltString>(由于泛型的尖括号内容不被顯示,用&lt 替換<)

3.泛型方法

類型變量放在修飾符後,傳回類型前面;

class ArrayAlg{
    public static <T> T getMiddle(T a){
        return a[a.length / 2];
    }
}
           

泛型方法可以定義在普通類或者泛型類中;

在執行個體化一個泛型類對象時,構造函數可以省去泛型類型;

4.類型變量的限定

有時,類或方法需要對變量類型進行限制,例如要求它必須是某個超類的子類,或者必須實作了某個接口,那麼被定義的泛型類作為接收方,也需要對傳入的類型變量T的值做一些限定和限制。

public class Pair<T entends SuperClass>{...}
public static <T entends Comparable> T min(T[] a){...}
           

T和綁定類型可以是類或接口,關鍵字extends相比較implemets更接近子類的概念;

限定類型用&分隔,逗号分隔類型變量;

限定中至多一個類,如果用該類作為限定,必須位于限定清單的第一個,可以擁有多個接口超類型;

5.限制與局限性

先來講一下什麼叫擦除?無論何時定義一個泛型類型,都自動提供一個相應的原始類型。原始類型的名字就是删去類型參數後的泛型類型名。擦除類型變量,并替換為限定類型(無限定的變量用object)

1.不能使用基本類型執行個體化類型參數

不能使類型參數替代基本類型,應使用基本類型對應的包裝器類型,當包裝器類型不能接受替換時,可以用獨立的類或者方法處理。

Pair<int> node = new Pair<int> (); // 非法
Pair<Integer> node = new Pair<Integer> ();//合法
           

2.運作時類型查詢隻适用于原始類型

所有類型查詢隻産生原始類型,無論何時使用instanceof或者泛型類型的強制類型轉換表達式都會報編譯器錯誤。

if(a instanceof Pair<T>) //error
Pair<String> p=(Pair<String>) a;//warning--can only test that a is a Pair
           

當類型變量不同時,假如使用getClass方法比較,可能會得到true,兩次調用getClass都将傳回Pair.class

3.不能建立參數化類型的數組

不能執行個體化參數化類型的數組,如下代碼會報錯

可以把它轉換為Object[]類型的數組,數組會記得它的元素類型,Object[] obj=table;

需要注意的是,隻是不允許建立這些數組,但聲明類型為Pair&ltString>[]的變量是合法的,不可以用new Pair&ltString>[10]初始化變量。

另外,如果需要收集參數化類型對象,可以使用AllayList:ArrayList<Pair&ltString>>

4.不能執行個體化類型變量

不能使用new T(…) , new T[…] , T.class這樣的表達式中的類型變量,可以通過反射Class.newInstance方法來構造泛型對象,如下代碼來支配class對象:

public static <T> Pair<T> makePair(Class<T> cl){
	try{
		return new Pair<> (cl.newInstance(),cl.newInstance())}
	catch(Exception ex){return null;}
}
           

可以使用如下調用:String.class實際上是Class&ltString>的唯一一個執行個體,方法能推斷出Pair的類型

5.泛型類的靜态上下文中類型變量無效

public class Pair<T> {
  private static T t;
  public static T get () { 
  /* 報錯
  提示: 'Pair.this' can not be referenced from a static context
  由于類型擦除後,隻有Pair類,包含一個t域,不能在靜态域或方法中引用類型變量
  */
    return T;
  }
}
           

6.不能抛出或者捕獲泛型類的執行個體

不能抛出或者捕獲泛型類對象,包括泛型類拓展Throwable也不合法,如下:

public class Pair {
  public static  <T extends Throwable> void doWork () {
    try {
   		 do work
    }catch (T t) {// 報錯 提示: Cannot catch type parameters
    }
  }
}
           

java異常進行中使用泛型可以消除對已檢查異常的檢查,如下是合法的:

public class Pair {
  public static  <T extends Throwable> void doWork  (T t) throws T {
    try {
     	do work
    }catch (Throwable realCause) {
     	throw  t;
    }
  }
}
           

6.泛型類型的繼承規則

必須注意泛型與Java數組之間的差別,還是用雇員及經理來說,我們可以将一個Manager[]數組指派給一個類型為Employee[]的變量,但是絕對不可以将Pair&ltManager>轉換為Pair&ltEmployee>,另外泛型類可以拓展或者實作其他的泛型類,例如ArrayList<T類可以實作List&ltT>, 用一張圖來形象的說明以上情況:

Java随筆—泛型程式設計引言1.為什麼使用泛型?2.定義簡單泛型類3.泛型方法4.類型變量的限定5.限制與局限性6.泛型類型的繼承規則7.通配符

7.通配符

固定的泛型類型可能使用中并不是很愉快,假設我們要編寫一個列印雇員的方法,但是向之前所說,不能将Pair&ltManager> 傳遞進來,是以Java的設計者提出了“通配符類型”來解決它。

1 .有限定通配符

public static void printBuddis(Pair<? entends Employee> p){
	Employee first = p.getFirst();
	...
}
           

有限定通配符存在一個問題,由于編譯器隻知道某個Employee的子類型,但是并不知道的具體什麼類型,是以不能調用setFirst()方法

2.超類型限定通配符

帶有超類型限定的通配符可以為方法提供參數,但是不能使用傳回值;也就是說帶有超類型限定的通配符可以向泛型對象寫入,帶有子類型限定的通配符可以從泛型對象讀取;

?super Manager該通配符限制為Manager的所有超類型

3.無限定通配符

Pair<?>和Pair的本質差別在:可以為任意Object對象調用原始的Pair類的setObject調用,有方法如下:

? getFirst()
void setFirst(?)//可以調用setFirst(null)
           

可以測試pair是否包括一個null引用

public static boolean hasNull(Pair<?> p){
	return p.getFirst()==null|| p.getSecond()==null;
}
           

4.通配符擷取

編寫一個交換一個pair元素的方法 :

public static void swap(Pair<?>)

通配符不是類型變量,是以不能再代碼中使用 ?為一種類型。代碼 ?t=p.getFrist(); 是非法的。我們可以編寫一個輔助方法swapHelper,如下:

public static <T> void swapHelper(Pair<T> p){
	T t=p.getFrist();
	p.setFirst(p.getSecond());
	p.setSecond(t);
}
           

現在我們可以在swap方法中調用swapHelper了

參考書籍:《Java核心卷Ⅰ》