泛型

1.爲什麼需要泛型?

我們看下面一個例子:
public class GenericTest {
    public static void main(String[] args) {
        List arrayList = new ArrayList();
        arrayList.add("zhang");
        arrayList.add("genericTest");
        arrayList.add(2017);
        
        for (int i=0; i<arrayList.size(); i++) {
            //1.java.lang.ClassCastException
            //String message = (String) arrayList.get(i);
            //System.out.println("message:"+message);
            //2.OK
            Object message = arrayList.get(i);
            System.out.println(message.getClass()+" "+"message:"+message);
        }
    }
}

       定義一個ArrayList類型的集合,先向其中加入兩個字符串類型的值,隨後加入一個Integer類型的集合,此處完全沒問題,因爲此時的arrayList默認的類型爲Object類型。在之後的for循環中由於忘記了arrayList類中含有Integer類型的元素,將Integer類型強制轉換爲String類型,出現1處的錯誤。

       在上面的編碼過程中,主要存在兩個問題:

       (1)將一個對象放入到一個集合中,集合不會記住此對象的類型,當再次從集合中取出此對象時,由於該對象在編譯類型爲Object類型,但其運行時類型依然爲其本身類型,所以在1處運行時出現 Java.lang.ClassCastException 異常,導致此類錯誤在編譯過程中不易被發現。

       (2)取出集合元素時,需要人爲強制轉化爲具體的目標類型

2.什麼是泛型?

        泛型,即“參數化類型”,顧名思義,就是將原來的具體的類型參數化,就像方法中的變量參數,此時類型也定義成參數形式(“類型形參”),然後在使用時傳入具體的類型(”類型實參”)。

        將上面的例子採用泛型的寫法。

public class GenericTest {
    public static void main(String[] args) {
        List<String> arrayList = new ArrayList<String>();
        arrayList.add("zhang");
        arrayList.add("genericTest");
        //arrayList.add(2017);1.提示編譯錯誤
        
        for (int i=0; i<arrayList.size(); i++) {
            String message = arrayList.get(i);//2
        	System.out.println("message:"+message);
        }
    }
}
     採用泛型寫法後,在1處加入一個Integer類型的對象時會出現編譯錯誤,通過List<String>限定arrayList集合中只能含有String類型的元素,因此在2處不需要強制類型轉換。

3.泛型接口、泛型類和泛型方法

(1)泛型類定義格式:

訪問權限 class 類名稱<泛型類型標識1,泛型類型標識2,..泛型類型標識n>{
    訪問權限 泛型類型標識 變量名稱;
    訪問權限 泛型類型標識 方法名稱(){};    
    訪問權限 返回值類型聲明 方法名稱(泛型類型標識 變量1,泛型類型標識 變量2,...){}   

(2)泛型對象的定義格式:

類名稱<具體類> 對象名稱 = new 類名稱<具體類>();      
簡單泛型類和方法使用:
package generic;

class Point<T> {
	private T var;
	
	public void setVar(T var) {
		this.var = var;
	}
	
	public T getVar() {
		return this.var;
	}
}

public class GenericClass {

	public static void main(String[] args) {
		Point<String> name = new Point<String>();
		name.setVar("zhang");
		System.out.println(name.getVar());
	}
}

在泛型接口、泛型類和泛型方法的定義過程中,常見 T、E、K、V 形式的參數表示泛型形參,思考:對於不同傳入的類型實參,生成的相應對象實例類型是不是一樣呢?

package generic;

public class GenericType {

	public static void main(String[] args) {
		Point<String> stringPoint = new Point<String>();
	    Point<Integer> integerPoint = new Point<Integer>();
	    System.out.println(stringPoint.getClass());//class generic.Point
	    System.out.println(integerPoint.getClass());//class generic.Point
	    System.out.println(stringPoint.getClass() == integerPoint.getClass());//true
	}
}

由此發現,在使用泛型時,雖然傳入了不同的泛型實參,但並沒有真正意義上生成不同的類型,泛型類在內存中只有一個,即還是原來最基本的類型(本例中爲Point)。泛型只作用於代碼編譯階段,成功編譯過後的class文件中是不包含任何泛型信息的,泛型信息不會進入到運行時階段,泛型類型在邏輯上可以看作是多個不同的類型,實際上都是相同的基本類型。

4.類型通配符

接着上面內容,思考:Point<Number>和Point<Integer>實際上都是Point類型,那麼在邏輯上,是否可以將二者看成具有父子關係的泛型類型呢?簡單例子:

package generic;

public class GenericGeneral {

	public static void main(String[] args) {
		Point<Number> name = new Point<Number>();
		Point<Integer> age = new Point<Integer>();
		getVar(name);
		//The method getVar(Point<Number>) in the type GenericGeneral is not applicable 
		//for the arguments (Point<Integer>)
		//getVar(age);1
	}
	
	public static void getVar(Point<Number> data) {
		System.out.println("data:"+data.getVar());
	}
}
編譯上面代碼,在1處提示錯誤信息,因此在邏輯上Point<Number>不能視爲Point<Integer>的父類。因此引入類型通配符的概念,邏輯上用來表示這種繼承關係。看下面類型通配符使用實例:

package generic;

public class Generic_01 {

	public static void main(String[] args) {
		Point<String> name = new Point<String>();
		Point<Integer> age = new Point<Integer>();
		Point<Number> number = new Point<Number>();
		getVar(name);
		getVar(age);
		getVar(number);
	}
	
	public static void getVar(Point<?> data) {
		System.out.println(data.getVar());
	}
}
類型通配符上限和類型通配符下限,比如在上面的例子上,定義一個同樣的相似的getVar方法,但是實參類型進一步限制,只能是Number類型及其子類,具體實例如下:

package generic;

public class Generic_01 {

	public static void main(String[] args) {
		Point<String> name = new Point<String>();
		Point<Integer> age = new Point<Integer>();
		Point<Number> number = new Point<Number>();
		getVar(name);
		getVar(age);
		getVar(number);
		
		//getUpperNumberVar(name);1
		getUpperNumberVar(number);2
		getUpperNumberVar(age);3
	}
	
	public static void getVar(Point<?> data) {
		System.out.println(data.getVar());
	}
	
	public static void getUpperNumberVar(Point<? extends Number> data) {
		System.out.println(data.getVar());
	}
}
在1處代碼將會出現錯誤提示,2和3處調用正常。

類型通配符下限形式爲Point<? super Numer>,其含義與類型通配符上限正好相反。


發佈了28 篇原創文章 · 獲贊 19 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章