java 泛型 java核心技術 讀書筆記

簡單泛型類

public class Pair<T> {
	private T first;
	private T second;
	
	public Pair(){
		first = null;
		second = null;
	}
	
	public Pair(T first,T second){
		this.first = first;
		this.second = second;
	}

	public T getFirst() {
		return first;
	}

	public void setFirst(T first) {
		this.first = first;
	}

	public T getSecond() {
		return second;
	}

	public void setSecond(T second) {
		this.second = second;
	}
}

提示:類型變量通常使用大寫形式,且比較短。在Java庫中,使用變量E表示集合的元素類型,K和V分別表示表的關鍵字與值的類型。T(需要進使用U和S)表示“任意類型”

 

泛型方法

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

泛型方法可以定義在普通類中,也可以定義在泛型類中。

 

類型變量的限定

class ArrayAlg{
	public static <T extends Comparable> Pair<T> minmax(T[] a){
		if(a == null || a.length == 0)
			return null;
		T min = a[0];
		T max = a[0];
		for(int i = 1; i < a.length; i++){
			if(min.compareTo(a[i]) > 0) min = a[i];
			if(max.compareTo(a[i]) < 0) max = a[i];
		}
		return new Pair<T>(min, max);
	}
}

以上代碼中,將T限制爲實現了Comparable接口的類。如:

	public static void main(String[] args) {
		Integer[] i = new Integer[]{12,345,563,24,64};
		Pair<Integer> pair = ArrayAlg.minmax(i);
		System.out.println(pair.getFirst() + "  " + pair.getSecond());
	}


如果將上面代碼中第二行修改爲int[] i = new int[]{12,345,563,24,64};,則第三行出現編譯錯誤。

一個類型變量或通配符可以有多個限定,如:

T extends Comparable & Serializable

 

泛型代碼和虛擬機
無論何時定義一個泛型類型,都自動提供原始類型。原始類型的名字就是刪去類型參數後的泛型類型名。擦除類型變量,並替換爲限定類型。

原始類型用第一個限定的類型變量來替換。如果沒有給定限定就用Object替換。如Pair<T>中用Object替換 T,

注意:class Interval<Serializable & Comparable>中,原始類型用Serializable替換T,而編譯器在必要時向Comparable插入強制類型轉換。爲了提高效率,應該將標籤接口(即沒有方法的接口)放在邊界列表的末尾。

 

當程序調用泛型方法時,如果擦除返回類型,編譯器插入強制類型轉換。如:

	Pair<Employee> buddies = ...;
	Employee buddy = buddies.getFirst();

  擦除getFirst的返回類型後將返回Object類型。編譯器自動插入Employee的強制類型轉換。當存取一個泛型域時也要強制類型轉換。如

Employee buddy = buddies.first;

 

方法public static <T extends Comparable> T min(T[] a)擦除後爲public static Comparable min(Comparable[] a);

注意:方法擦除將帶來兩個複雜問題,請查看Java核心技術

Java泛型轉換:

虛擬機中沒有泛型 ,只有普通的類和方法

所有的類型參數都用到它們的限定類型替換

橋方法(查看Java核心技術)被合成保持多態

爲保持類型安全性,必要時插入強制類型轉換

 

約束與侷限性

1.不能用基本類型實例化類型參數

只要8種基本類型,當包裝類型不能接受替換時,可以使用獨立的類和方法處理它們。

2.運行時類型查詢只適用於原始類型

  Pair<Integer> a = new Pair<Integer>();
  a instanceof Pair<String>;

 上面代碼第2行將報錯。

 

3.不能拋出也不能捕獲泛型類實例

泛型類擴展Throwable都不合法。如public class Problem<T> extends Exception{      }       //ERROR--不能通過編譯。

不能在catch子句中使用類型變量,如

	public static <T extends Throwable> void dowork(Class<T> t){
		try{
			do work
		}
		catch(T e){//編譯錯誤  -- 不能捕獲這種類型
			
		}
	}

但,在異常聲明中可以使用類型變量,如:

	public static <T extends Throwable> void doWork(T t) throws T{//OK
		try{
			do work
		}
		catch(Throwable realCause){
			t.initCause(realCause);
			throw t;
		}
	}


4.參數化類型的數組不合法

不能聲明參數化類型的數組,如:Pair<String>[] table = new Pair<String>[10];  //ERROR

5.不能實例化類型變量

 6.泛型類的靜態上下文中類型變量無效

public class Singleton<T> {
	public static T getSingInstance(){
		
	}
}

上面代碼中方法getSingInstance無法通過編譯。

7.注意擦除後的衝突

public class Pair<T> {
	public boolean equals(T value){
		return first.equals(value)&&second.equals(value);
	}
}

從概念上講,Pair有兩個equals方法:

boolean equals(T)//定義在Pair<T>

boolean equals(Object)//從Object中繼承

方法擦除將導致兩個方法相沖突。可以重新命名引發錯誤的方法。


通配符類型(用於方法參數)

通配符Pair<? extends Employee>表示任何任何Pair類型,它的類型參數是Employee的子類,如Pair<Manager>,但不能是Pair<String>

注意:類型Pair<Manager>是Pair<? extends Employee>的子類型,而ArrayList<Manager>並不是ArrayList<Employee>的子類

如:

	public static void printBuddies(Pair<? extends Employee> p){
		Employee first = p.getFirst();
		Employee second = p.getSecond();
		System.out.println(first + "   " + second);
	}
可以這樣調用 :printBuddies(new Pair<Manager>());

 

超類型限定:? super Manager這個通配符限制爲Manager的所有超類型

注意:當從超類型限定通配符中取出值時,只能將它賦給Object,如:Pair<? super Manager> p通配符中,Object o = p.getFirst();因爲調用getFirst,返回的對象類型得不到保證。

直觀地說,帶有超類限定的通配符可以向泛型對象寫入,如Pair<? super Manager> p中,可以這樣調用:p.setFirst(new Manager());而帶有子類型限定的通配符可以從泛型對象讀取,如Pair<? extends Employee> p通配中,可以這樣調用:Employee first = p.getFirst();


無限定通配符Pair<?>有方法:

? getFirst();

void setFirst();

getFirst的返回值只能賦給一個Object,而setFirst方法不能被調用,甚至不能用Object調用。注意:Pair可以用任意Object對象調用原始的Pair類的setFirst方法。

無限定通配符對於很多簡單的操作非常有用。如,下面這個方法將用來測試一個Pair是否包含了指定的對象,它不需要實際的類型

    public static boolean hasNulls(Pair<?> p){

        return p.getFirst() == null || p.getSecond() == null;

    }



 通配符捕獲

有時我們需要使用通配符中的類型,如編寫 一個交換Pair元素的方法public static void swap(Pair<?> p),但?不能作爲一個類型,就是說

       ? t = p.getFirst();

        p.setFirst(p.getSecond());

        p.setSecond(t);


 上面代碼無法通過編譯。

但我們可以通過一個輔助方法來完成工作,完整代碼如下:

 public static void swap(Pair<?> p){

        swapHelper(p);

    }

    

    public static <T> void swapHelper(Pair<T> p){

        T t = p.getFirst();

        p.setFirst(p.getSecond());

        p.setSecond(t);

    }



反射和泛型

Class類是泛型的。如String.class是Class<String>的唯一對象。類型參數十分有用。

	public static <T> T createInstance(Class<T> clazz) throws InstantiationException, IllegalAccessException{
		return clazz.newInstance();
	}
爲了表達泛型類型聲明,Java SE 5.0在java.lang.reflect包中提供了一個新的接口Type,包括

Class類,描述具體類型

TypeVariable接口,描述類型變量

WildcardType接口,描述通配符

ParameterizedType接口,描述泛型類型或接口類型

GenericArrayType接口,描述泛型數組。

具體情況請查閱API



 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章