1. 程式人生 > >泛型原理、概念、使用

泛型原理、概念、使用

泛型簡介/原理

1、從JDK1.4開始是沒有泛型的概念的,那怎麼做呢?如下列程式碼
	List list = new ArrayList();
	list.add("string");
	list.add(11);
	String name = (String)list.get(0);
	Integer number = (Integer)list.get(1);

△這樣子做有什麼缺點呢
存的都是object,需要強轉,轉換時可能發生異常

2、JDK1.5以後的寫法
	List<String> list = new ArrayList();
	list.
add("string"); list.add("string2"); String cnBlogs = list.get(0); String myWebSite = list.get(1);

總結: 泛型就是將型別引數化,其在編譯時才確定具體的引數。在上面這個例子中,這個具體的型別就是 String。
可以看到我們在建立 List 集合的時候指定了 String 型別,這就意味著我們只能往 List 集合中存放 String 型別的資料。
而當我們指定泛型之後,我們去取出資料後就不再需要進行強制型別轉換了,這樣就減少了發生強制型別轉換的風險。
這就是Sun公司為了JAVA語言更嚴格安全。

3、泛型的原理:

能看出這段程式碼的結果是什麼嗎?

		ArrayList<String> a = new ArrayList<String>();
		ArrayList<Integer> b = new ArrayList<Integer>();
		Class c1 = a.getClass();
		Class c2 = b.getClass();
		System.out.println(c1 == c2); 

△答案:是true,泛型只存在於編譯階段,而不存在於執行階段。c1和c2獲取到的都是java.util.ArrayList

,Integer 只不過是 JVM在編譯的時候會進行轉換,編譯後的檔案時沒有泛型概念的,也就是SUN嚴格規範了JAVA語言更安全。

△擴充套件:上面我們只是說了泛型在集合中的使用方式,但其實泛型的應用範圍不僅僅只是集合,還包括類、方法、Map 介面等等

泛型的應用場景

泛型類
1、泛型類一般使用字母 T 作為泛型的標誌
	public class GenericClass<T> {
	    private T object;
	    public T getObject() {
	        return object;
	    }
	    public void setObject(T object) {
	        this.object = object;
	    }
	}

△擴充套件: MAP類中也有標誌 K-V

	public class GenericMap<K, V> {
	    private K key;
	    private V value;
	    public void put(K key, V value) {
	        this.key = key;
	        this.value = value;
	    }
	}
	public static void main(String[] args) {
	        GenericMap<Integer, String> team = new GenericMap<>();
	        team.put(1, "YaoMin");
	        team.put(2, "Me");
	        GenericMap<String, Integer> score = new GenericMap<>();
	        score.put("YaoMin", 88);
	        score.put("Me", 80);
	    }

△擴充套件: 那麼像每次定義列舉型別,都是數值什麼的那麼我們怎麼定義中文描述

不會動的載入中...
泛型方法
1、泛型方法一般也用字母 T 作為泛型的標誌
	public class GenericMethod {
	    public static <T> T getObject(Class<T> clz) throws InstantiationException, IllegalAccessException{
	        T t = clz.newInstance();
	        return t;
	    }
	}
	public static void main(String[] args) throws Exception{
	    GenericMethod genericMethod = getObject(GenericMethod.class);
	    System.out.println("Class:" + genericMethod.getClass().getName());
	}

泛型集合 拉拉最頂上↑

泛型萬用字元

1、除了泛型類、泛型方法之外,泛型還有更加複雜的應用,如:
	List<? extends Number> list = new ArrayList();
	List<? super Number> list = new ArrayList();

△上面的 extends 和 super 關鍵字其實就是泛型的高階應用:泛型萬用字元。
△但在講泛型萬用字元之前,我們必須對編譯時型別和執行時型別有一個基本的瞭解,才能更好地理解萬用字元的使用。

	public class Tree {}
	public class Fruits extends Tree{}
	public class Banana extends Fruits{}
	public class Plate<T> {
	    T t;
	    public T get(){
	        return  t;
	    }
	    public void set(T t){
	        this.t=t;
	    }
	}
2、來看看泛型的劣優

(1)Error 泛型不支援向上轉型

       /* Plate<Fruits> banana=new Plate<Banana>();*/

(2) ? extends 用法

	Plate<? extends Fruits> bananaExtens=new Plate<Banana>();
	/*//Error不允許寫入 不管是父類還是子類
	bananaExtens.set(new Fruits());*/      
	/**就這一步,未執行時根本不知道你例項的那個子類,那 JVM 就乾脆什麼都不給放,避免出錯。**/
	//只允許讀取
	bananaExtens.get();

(3) ? super 用法 宣告子類,其例項物件可以是父類或是父類以上的級別,例Object也是可以的

        Plate<? super Banana> bananaFruSuper=new Plate<Fruits>();
        Plate<? super Banana> bananaTreSuper=new Plate<Tree>();
	   //可以寫入,但是隻能寫入宣告物件中同級或者以下的類 子類,子子類
		//就這步,為執行時JVM既然不能知道例項物件是哪個,但是JVM能確定Banana的子類能轉換成Banana,但是父類卻不能轉換()
        bananaTreSuper.set(new Banana());
        /*//都是高於宣告物件級別
        bananaTreSuper.set(new Tree());
        bananaTreSuper.set(new Fruits());*/
        
        /*//Error也是高於物件級別
        bananaFruSuper.set(new Fruits());*/
        
         /* //Error 能取但是,只能用Object 來接收
         Banana b= bananaFruSuper.get();
         Fruits f= bananaFruSuper.get();
         */
         
         Object o= bananaFruSuper.get();

PESC原則

在泛型程式設計時,使用部分限定的形參時,<? super T>和<? extends T>的使用場景容易混淆,PECS原則可以幫助我們很好記住它們:
生產者(Producer)使用extends,消費者(Consumer)使用super。
留下一段程式碼加深印象(來自JDK 8 Collections.copy()原始碼)

public static <T> void copy(List<? super T> dest, List<? extends T> src) {
        int srcSize = src.size();
        if (srcSize > dest.size())
            throw new IndexOutOfBoundsException("Source does not fit in dest"); 

        if (srcSize < COPY_THRESHOLD ||
            (src instanceof RandomAccess && dest instanceof RandomAccess)) {
            for (int i=0; i<srcSize; i++)
                dest.set(i, src.get(i));
        } else {
            ListIterator<? super T> di=dest.listIterator();
            ListIterator<? extends T> si=src.listIterator();
            for (int i=0; i<srcSize; i++) {
                di.next();
                di.set(si.next());
            }
        }
}

參考於:
https://www.cnblogs.com/chanshuyi/p/deep_insight_java_generic.html