Java - 特性 - 泛型 - 整理

泛型是Java最具影響力的新特性之一,Java程序員需要深入理解這一特性。

   |--   從字面上看:泛型就是泛泛的指定對象所操作的類型,而不像常規方式一樣使用某種固定的類型去指定。

   |--   從本質上看:泛型就是參數化類型,在創建類、接口、方法時可以用類型參數指定他們所要操作的數據類型。

   |--   類型參數最終都是要被實現的。


/**一個泛型類的簡單實例**/

public class Gen<T> { //這裏的T是類型參數的名稱,又叫標記,是實際類型的佔位符
	
	private T ob;	
	
	Gen(T ob){
		this.ob = ob;
	}	
	
	public T getOb(){
		return ob;
	}	
	
	public T setOb(T ob){
		this.ob = ob;
		return this.ob;
	}
	/*
	 * 因爲Object是所有類的基類,所以這裏可以使用getClass()和getName()。
	 * 如果使用到其他類的方法,是不行的。因爲編譯器並不知道T代表的具體類型。
	 * 如果要想使用其他類的方法,T要限定範圍,也就是有界類型。
	 * 
	 * */
	public void showtype(){
		System.out.println("ob的類型是:"+ob.getClass().getName());
	}
}

/**下面是一個泛型類不合理的實現方式**/

public class GenDemo {

	public static void main(String[] args) {

		//Gen<Integer>將<Integer>傳遞給力Gen<T>的T。因爲是在類上聲明的所以對整個類都有效。
		Gen<Integer> Ob1 = new Gen(new Integer(123));
		Integer i = Ob1.getOb();
		System.out.println(i);
		Ob1.showtype();

		//Gen沒有指定要傳遞的數據類型,編譯後會被Object替換。new Gen<String>只是在創建Gen實例時指定Gen副本的T。
7		//這時構造函數相當於 Object ob = "123456"。Jdk7以後創建實例好像已經不用重新聲明瞭、
		Gen ob2 = new Gen<String>("123456");
		Object s = ob2.getOb();
		System.out.println(s);
		ob2.showtype();//java.lang.String。getClass()獲取的是創建"123456"這個對象的類。
		
		//ob1=ob2;/**同一種泛型可以對應多個版本(因爲參數類型是不確定的),但是不同版本的泛型類實例是不兼容的。**/

	}

}


需要注意的是至始至終只有一個 Gen 類。Java編譯器實際上只是擦除了所有泛型類信息,將之替換成必須的類型。


/**一個加深對泛型理解的例子**/

public class Tool{
	
	public <T> void add(Collection<? super T> a,T b){
//		<?>可以引用各種參數化的類型,可以調用與參數無關的方法,不能調用與參數有關的方法
//		a.addAll(a);// 編譯失敗。 boolean add(E e) 		 
		a.remove(b);// boolean remove(Object o) 			
	}
}

public class Test {

	public static void main(String[] args) {
		
		Tool tool =new Tool();
		//不完全確定list的類型。至少可以是Number,不管是什麼類型,完全可以裝下Integer
		List<? super Number> list = new ArrayList<Number>();
		//完全不確定list的類型。如果是Long就裝不下,Integer
//		List<? extends Number> list = new ArrayList<Number>();//編譯失敗		
		list.add(new Integer(123));		
		tool.add(list,123);
	}
}



從上面可以看出泛型的一些使用規則:


      |--   泛型的類型參數只能是類類型(包括自定義類),不能是基礎數據類型。

      |--   同一種泛型可以對應多個版本(因爲參數類型是不確定的),但是不同版本的泛型類實例是不兼容的

      |--  泛型可以是靜態的也可以是非靜態的,靜態內不能使用類型參數。靜態方法的返回類型不能是類型參數

      |--   不能創建泛型異常類。

      |--   類型參數不能被實例化。


使用泛型的好處:


      |--   創建類型安全的代碼,將運行時錯誤轉換成編譯時錯誤。
      |--   簡化了處理過程,不再需要顯示的使用 強制類型轉換,所有類型轉換都是自動、隱式進行的。
      |--   擴展了代碼的重用能力。


泛型的語法:


      |--   聲明泛型類:class class_name<type_param_list>

            |--   引用的語法:class<type_arg_list> var_name = newclass<type_arg_list>(cons_arg_list);


泛型的有界類型:

   |--   告訴編譯器類型參數的範圍。我們在實例化對象時所傳遞的類型也必須在這個範圍之內。

   |--   限定上界 : <T extends Number>範圍:Number及其子類。

   |--   限定下界 : <T super Number> 範圍:Number及其父類。

   |--   限制方法和可以操作對象的類型。


泛型的通配符:

   |--   在泛型中可以使用通配符參數,用?來指定。表示未知類型

   |--   解決了指定佔位符操作數據類型的侷限性。可以同時操作多種數據類型。

   |--   <T extends Number> 和 <?extends Number> 的區別。從範圍來看他們是相等的。但是。。。

   |--   通配符是不能被用做最終類型的,類型參數最終都是要被實現的。


/** 一個簡單的有界類 **/

/*
 * 一個簡單的計算數組和的類
 * 實現:
 * 要操作的數據類型很多,想到 泛型。
 * 所有數字類型都是Number的子類。
 * 在計算時要把對象轉成基本數據類型進行運算。
 * 所以要限定標記的範圍,告訴編譯器我們要使用的數據類型都是Number的子類。
 * 
 * */

// 只有同一種數據類型纔可以比較 T 
 public class Stats<T extends Number> {
	private T[] nums;

	Stats(T[] n) {
		nums = n;
	}

	T[] get() {
		return nums;
	}
	/*
	 *判斷兩個數組的大小
         *
         *這裏的Stats<?>是函數的參數,是爲了更安全的表述ob的類型。	
	 *
	 */
	public double compareSum(Stats<?> ob) {
		
		return add() - ob.add();
	}

	public double add() {

		double sum = 0.0;

		for (int i = 0; i < nums.length; i++) {
			sum += nums[i].doubleValue();
		}

		return sum;
	}

}

public static void main(String[] args) {
		
		Integer[] nums = {2,3,1,5,6,3,4};
//		Integer nums[] = {2,3,1,5,6,3,4,10};//數組的另一種表現形式。
		Number[] nums2 = {1.1,2.1,3.1,4};
		
		Stats<Integer> stats = new Stats<Integer>(nums);
		Stats<Number> stats2 = new Stats<Number>(nums2);
		double sum = stats.add();
		double sum2 = stats2.add();
		System.out.println(sum + "--" +sum2);	
		
		System.out.println(stats.compareSum(stats2));	
		
	}


泛型方法:

   |--   可以將類方法泛型化,即使他們的類不是泛型類。

   |--   泛型方法可以指定參數類型,返回值類型,參數之間的關係。

/*
 * 用泛型方法判斷任意一個數是否在一個數組中  
 * 
 */
public class GenFunc {

	public static <T, Y extends T> boolean isIn(T t, Y[] y) {

		for (int i = 0; i < y.length; i++) {

			if (t.equals(y[i]))	return true;
		}

		return false;
	}

	public static void main(String[] args) {
		
		Integer[] nums = {21,32,545,87} ;
		int i = 21;
		if(isIn(i,nums)) System.out.println(i + "在nums中");		
		
		String[] strs = {"123","456","789"} ;
		String s = "123";
		if(isIn(s,strs)) System.out.println(s + "在nums中");			
		
	}

}

      |--   類型參數在方法返回前聲明

   |--  泛型可以是靜態的也可以是非靜態的,靜態方法不能使用定義在類上的類型參數。只能定義在方法上、

   |--   <T, Y extends T> 限定了 Y 的範圍,保證了 T 和 Y 是同種數據類型。(強制類型安全


泛型接口:

   |--   泛型接口可以針對不同類型的數據進行實現。。。

   |--   一般而言如果實現了泛型接口,那麼類也必須泛型化,至少應該實現接口的類型參數

   |--  子類都必須向上傳遞超類所需要的類型參數。

/*
 * GenInterface功能擴展。
 * 希望GenInterface可以實現計算出對象數組的最值功能。
 * 想到了接口
 * 對象比較想到Comparable
 * Comparable<T>的子類都具有比較功能。
 * 兩個對象比較,必須數據類型一致,並且是Comparable<T>的子類纔可以比較
 * 
 * */
/***
 * 比較大小要返回值,返回值類型不確定。 泛型。 
 * 只有同一種數據類型纔可以比較 T 
 * 創建接口的時候最好限定類型範圍,這符合邏輯思維
 ****/
interface minMax<T extends Comparable<T>> {
	T min(T[] vals);

	T max(T[] vals);
}

/***********
 * 
 * 子類必須現實接口的類型 minMax<T>,把T傳遞個接口
 * 不實現默認Object 泛型就沒意義了 
 * 如果你覺得這裏有點牽強,只要保證子類在範圍內不就行了。那就錯了。
 * 如果沒有把類型傳遞給接口:那麼就相當於
 * 接口的函數是:Object min(Object vals)
 * GenInterface:T min(T[] vals) 這是不行的。。。
 * 
 ******/
public class GenInterface<T extends Comparable<T>> implements minMax<T> {

	public T min(T[] vals) {
		
		T min = vals[0];
		for (int i = 1; i < vals.length; i++) 
			if (min.compareTo(vals[i]) > 0) min = vals[i];
		return min;
	}

	public T max(T[] vals) {
		
		T max = vals[0];
		for (int i = 1; i < vals.length; i++)
			if (max.compareTo(vals[i]) < 0)	max = vals[i];			
		return max;
	}

	public static void main(String[] args) {
		
		String[] vals = {"asdas","bsdas","esdasd","ffasd"};
		GenInterface<String> demo1 = new GenInterface<String>();
		String min = demo1.min(vals);
		String max = demo1.max(vals);
		System.out.println(min +"--"+ max);
	
	}

}

泛型類實例的強制類型轉換:

   |--   只有當泛型類實例的類型相互兼容,類型參數也相同的時候纔可以。

   |--   但是這樣轉換有意義?

   |--   GenInterface<String> demo1 = new GenInterface<String>();

    |--   GenInterface<Integen> demo2 = new GenInterface<Integen>();

   |--   雖然都GenInterface類型實例,但是已經是兩種完全不兼容的對象了、這是泛型提高代碼重用帶來的、


泛型工作的原理:

   |--   爲了兼容以前的老版本,java使用擦拭實現泛型。怎麼擦拭

   |--   使用具體類型替換類型參數,如果沒有顯示的指定,就使用Object。


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