黑馬程序員---java基礎加強---jdk1.5新特性之泛型

----------- android培訓java培訓、java學習型技術博客、期待與您交流! -----------

一泛型的基本運用及內部原理

泛型,是提供給javac編譯器使用的,可以限定集合中的輸入類型,讓編譯器擋住源程序中的非法輸入,編譯器編譯帶類型說明的集合時,會去除掉“類型"信息,使程序運行效率不受影響,對於參數化的泛型類型,getClass()方法的返回值和原始類型完全一樣。由於編譯生成的字節碼會去掉泛型的類型信息,只要能跳過編譯器,就可以往某個泛型集合中加入其它類型的數據,例如,用反射得到集合,再調用其add方法即可。

ArrayList <E>類定義和ArrayList<Integer>類引用中涉及如下術語:
        整個稱爲ArrayList<E>泛型類型
        ArrayList<E>中的E稱爲類型變量或類型參數
        整個ArrayList<Integer>稱爲參數化的類型
        ArrayList<Integer>中的Integer稱爲類型參數的實例或實際類型參數
        ArrayList<Integer>中的<>讀作typeof
        ArrayList稱爲原始類型
參數化類型和原始類型的兼容性
        參數化類型可以引用一個原始類型的對象,編譯報告警告,例如
                Collection<String> c = new Vector();
        原始類型可以引用一個參數化類型的對象,編譯報告警告,例如
                Collection c = new Vector<String> ();
參數化類型不考慮類型參數的繼承關係
        Vector<String> v = new Vector<Object>();錯誤
        Vector<Object> v = new Vector<String>(); 錯誤
在創建數組實例時,數組的元素不能使用參數化類型
        Vector<Integer> vectorList[]= new Vector<Integer>[10];錯誤

判斷對錯:原因,編譯器嚴格按語法檢查,不考慮執行時候
        Vector v = new Vector<String>();   編譯通過,原始類型=參數化類型
        Vector<Object> v1 = v;編譯通過,參數化類型= 原始類型


二 ? 通配符

    問題:定義一個方法,該方法用於打印任意參數化類型的集合中的所有數據
    錯誤方式:
            public static void printCollection(Collection<Object> cols)
            {
                    for(Object obj:cols)
                    {
                            System.out.println(obj);
                    }
                    cols.add("String");//沒錯,因爲是Object  所以可以add  String
                    cols = new HashSet<Date>();//錯誤,上面是Object   下面是Date  不能這樣做
    正確方式:
            public static void printCollection(Collection<?> cols)
            {
                    for(Object obj:cols)
                    {
                            System.out.println(obj);
                    }
                    cols.add("String");//錯誤,因爲不知道?是什麼類型,不一定匹配String類型
                    cols.size();//正確,此方法與類型參數沒有關係
                    cols = new HashSet<Date>();//正確
                    //使用?通配符可以引用其他各種參數化的類型,?通配符定義的變量主要用於引用,可以調用與參數化無關的方法,不能調用與參數化有關的方法
            }
            }


泛型中的?通配符的擴展
    限定通配符的上邊界:
            正確:Vector<? extends Number> x = new Vector<Integer>();
            錯誤:Vector<? extends Number> x = new Vector<String>();
    限定通配符的下界:
            正確:Vector<? super Integer> x = new Vector<Number>();
            錯誤:Vector<? super Integer> x = new Vector<Byte>();
    提示:
            限定通配符總是包括自己。

三 自定義泛型方法

自定義泛型,由C++的模板函數引入自定義泛型
    如下函數的結構很相似,僅類型不同
            int add(int x,int y)
            {
                    return x+y;
            }
            int add(float x,float y)
            {
                    return x+y;
            }
            int add(double x,double y)
            {
                    return x+y;
            }
            
交換數組中的兩個元素的位置的泛型方法語法定義如下:
static <E> void swap(E[] a,int i ,int j)
{
        E t = a[i];
        a[i] = a[j];
        a[j] = t;
}
用於防止泛型的類型參數的尖括號應出現在方法的其他所有修飾符之後和在方法的返回值類型之前,也就是緊鄰返回值之前,按照慣例,類型參數通常用單個大寫字母表示。
只有引用類型才能作爲泛型方法的實際參數,swap(new int[2]{3,2},1,2),語句會報編譯錯誤
除了在應用泛型時可以使用extends限定符,在定義泛型時也可以使用extends限定符,例如:
        Class.getAnnotation()方法的定義,並且可以用&來指定多個邊界,如
        <V extends Serializable&cloneable> void method(){}
普通方法、構造方法和靜態方法中都可以使用泛型
也可以用類型變量來表示異常,稱爲參數化的異常,可以用於方法的throws列表中,但是不能用於catch後的()中
在泛型中可以同時又多個類型參數,在定義他們的<>中用逗號分開,例如:
        public static<K,V> V getValue(K key){return map.get(key);}

java中的泛型類型類似於c++中的模板,但是這種相似性僅限於表面,java語言中的泛型基本上完全是在編譯器中用於編譯器執行類型檢查和類型推斷,然後生成普通的非凡行的字節碼,這種實現技術爲擦除erasure(編譯器使用泛型類型保證類型安全,然後在生成字節碼之前將其清除),這是因爲擴展虛擬機指令來支持泛型被認爲是無法接受的,這回爲java廠商升級其jvm造成難以逾越的障礙。所以,java的泛型採用了可以完全在編譯器中實現的擦除方法。


四自定義泛型類

自定義泛型類的應用
如果類的實例對象中的多處都要用到同一個泛型參數,即這些地方引用的泛型類型要保持同一個實際類型時,這時候就要採用泛型類型的方式進行定義,也就是類級別的泛型,語法格式如下:
        public class GenericDao<T>
        {
                private T field1;
                public void save(T obj)
                {
                }
                public T getById(int id)
                {
                }
        }
類級別的泛型時根據引用該類名時指定的類型信息來參數化類型變量的,例如,如下兩種方式都課可以
        GenericDao<String> dao = null;
        new GenericDao<String>();
注意:在堆泛型類型進行參數化時,類型參數的實例必須是引用類型,不能是基本類型
            當一個變量被聲明爲泛型時,只能被實例變量和方法調用(還有內嵌類型),而而不能被靜態變量和靜態方法調用。因爲靜態成員是被所有參數化的類鎖共享的,所以靜態成員不應該有類級別的類型參數。
         public static <E> void update2()//可以,給該靜態方法加泛型
{
}
public static void update(T obj)//錯誤,這裏不能調用泛型參數
{
}
問題:類中只有一個方法需要使用泛型,是使用類級別的翻新還是使用方法級別的泛型?方法級別。



通過反射獲得泛型的實際類型參

得到參數化類型的方法,通過反射得到該方法的字節碼,然後得到該方法中參數的泛型類型


----------- android培訓java培訓、java學習型技術博客、期待與您交流! -----------


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