<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    午后星期午

    java 泛型深入

    泛型的好處:

        泛型的主要好處就是讓編譯器保留參數(shù)的類(lèi)型信息,執(zhí)行類(lèi)型檢查,執(zhí)行類(lèi)型轉(zhuǎn)換(casting)操作,編譯器保證了這些類(lèi)型轉(zhuǎn)換(casting)的絕對(duì)無(wú)誤。


            
    /******* 不使用泛型類(lèi)型 *******/
            List list1 
    = new ArrayList();
            list1.add(
    8080);                                  //編譯器不檢查值
            String str1 = (String)list1.get(0); //需手動(dòng)強(qiáng)制轉(zhuǎn)換,如轉(zhuǎn)換類(lèi)型與原數(shù)據(jù)類(lèi)型不一致將拋出ClassCastException異常
            
            
    /******* 使用泛型類(lèi)型 *******/
            List
    <String> list2 = new ArrayList<String>();
            list2.add(
    "value");                 //[類(lèi)型安全的寫(xiě)入數(shù)據(jù)] 編譯器檢查該值,該值必須是String類(lèi)型才能通過(guò)編譯
            String str2 = list2.get(0); //[類(lèi)型安全的讀取數(shù)據(jù)] 不需要手動(dòng)轉(zhuǎn)換
            


    泛型的類(lèi)型擦除:

        Java 中的泛型只存在于編譯期,在將 Java 源文件編譯完成 Java 字節(jié)代碼中是不包含泛型中的類(lèi)型信息的。使用泛型的時(shí)候加上的類(lèi)型參數(shù),會(huì)被編譯器在編譯的時(shí)候去掉。

        這個(gè)過(guò)程就稱(chēng)為類(lèi)型擦除(type erasure)。


            List
    <String>    list1 = new ArrayList<String>();
            List
    <Integer> list2 = new ArrayList<Integer>();
            
            System.out.println(list1.getClass() 
    == list2.getClass()); // 輸出結(jié)果: true
            System.out.println(list1.getClass().getName()); // 輸出結(jié)果: java.util.ArrayList
            System.out.println(list2.getClass().getName()); // 輸出結(jié)果: java.util.ArrayList
            

    在以上代碼中定義的 List<String> 和 List<Integer> 等類(lèi)型,在編譯之后都會(huì)變成 List,而由泛型附加的類(lèi)型信息對(duì) JVM 來(lái)說(shuō)是不可見(jiàn)的,所以第一條打印語(yǔ)句輸出 true,

    第二、第三條打印語(yǔ)句都輸出 java.util.ArrayList,這都說(shuō)明 List<String> 和 List<Integer> 的對(duì)象使用的都是同一份字節(jié)碼,運(yùn)行期間并不存在泛型。

    來(lái)看一個(gè)簡(jiǎn)單的例子:


    package test;

    import java.util.List;
    /**
     * -----------------------------------------
     * @描述  類(lèi)型擦除
     * @作者  fancy
     * @郵箱  fancydeepin@yeah.net
     * @日期  2012-8-25 <p>
     * -----------------------------------------
     
    */
    public class GenericsApp {

        
        
    public void method(List<String> list){
            
        }
        
        
    /*
         * 編譯出錯(cuò),這兩個(gè)方法不屬于重載,由于類(lèi)型的擦除,使得這兩個(gè)方法的參數(shù)列表的參數(shù)均為L(zhǎng)ist類(lèi)型,
         * 這就相當(dāng)于同一個(gè)方法被聲明了兩次,編譯自然無(wú)法通過(guò)了
         * 
        public void method(List<Integer> list){
            
        }
        
    */
        
    }

    以此類(lèi)為例,在 cmd 中 編譯 GenericsApp.java 得到字節(jié)碼,然后再反編譯這份字節(jié)碼:



    從圖中可以看出,經(jīng)反編譯后的源碼中 method 方法的參數(shù)變成了 List 類(lèi)型,說(shuō)明泛型的類(lèi)型被擦除了,字節(jié)碼文件中不存在泛型,也就是說(shuō),運(yùn)行期間泛型并不存在,它在

    編譯完成之后就已經(jīng)被擦除了。


    泛型類(lèi)型的子類(lèi)型:

        泛型類(lèi)型跟其是否是泛型類(lèi)型的子類(lèi)型沒(méi)有任何關(guān)系。


            List
    <Object> list1;
            List
    <String> list2;
            
            list1 
    = list2; // 編譯出錯(cuò)
            list2 = list1; // 編譯出錯(cuò)

    在 Java 中,Object 類(lèi)是所有類(lèi)的超類(lèi),自然而然的 Object 類(lèi)是 String 類(lèi)的超類(lèi),按理,將一個(gè) String 類(lèi)型的對(duì)象賦值給一個(gè) Object 類(lèi)型的對(duì)象是可行的,

    但是泛型中并不存在這樣的邏輯,泛型類(lèi)型跟其是否子類(lèi)型沒(méi)有任何關(guān)系。


    泛型中的通配符(?):

        由于泛型類(lèi)型與其子類(lèi)型存在不相關(guān)性,那么在不能確定泛型類(lèi)型的時(shí)候,可以使用通配符(?),通配符(?)能匹配任意類(lèi)型。


            List
    <?> list;
            List
    <Object> list1 = null;
            List
    <String>  list2 = null;
            
            list 
    = list1;
            list 
    = list2;


    限定通配符的上界:


            ArrayList
    <? extends Number> collection = null;
            
            collection 
    = new ArrayList<Number>();
            collection 
    = new ArrayList<Short>();
            collection 
    = new ArrayList<Integer>();
            collection 
    = new ArrayList<Long>();
            collection 
    = new ArrayList<Float>();
            collection 
    = new ArrayList<Double>();
            

     ? extends XX,XX 類(lèi)是用來(lái)限定通配符的上界,XX 類(lèi)是能匹配的最頂層的類(lèi),它只能匹配 XX 類(lèi)以及 XX 類(lèi)的子類(lèi)。在以上代碼中,Number 類(lèi)的實(shí)現(xiàn)類(lèi)有:

    AtomicInteger、AtomicLong、 BigDecimal、 BigInteger、 Byte、 Double、 Float、 Integer、 Long、 Short ,因此以上代碼均無(wú)錯(cuò)誤。


    限定通配符的下界:


            ArrayList
    <? super Integer> collection = null;
            
            collection 
    = new ArrayList<Object>();
            collection 
    = new ArrayList<Number>();
            collection 
    = new ArrayList<Integer>();
            

     ? super XX,XX 類(lèi)是用來(lái)限定通配符的下界,XX 類(lèi)是能匹配的最底層的類(lèi),它只能匹配 XX 類(lèi)以及 XX 類(lèi)的超類(lèi),在以上代碼中,Integer 類(lèi)的超類(lèi)有:

    Number、Object,因此以上代碼均能通過(guò)編譯無(wú)誤。


    通過(guò)反射獲得泛型的實(shí)際類(lèi)型參數(shù):

        java.lang.Class 類(lèi)從 Java 1.5 起(如果沒(méi)記錯(cuò)的話(huà)),提供了一個(gè) getGenericSuperclass() 方法來(lái)獲取直接超類(lèi)的泛型類(lèi)型


    package test;

    import java.lang.reflect.ParameterizedType;
    /**
     * -----------------------------------------
     * @描述  泛型的實(shí)際類(lèi)型參數(shù)
     * @作者  fancy
     * @郵箱  fancydeepin@yeah.net
     * @日期  2012-8-25 <p>
     * -----------------------------------------
     
    */
    public class Base<T> {

        
    private Class<T> entityClass;
        
        
    //代碼塊,也可將其放置到構(gòu)造子中
        {
            entityClass 
    =(Class<T>)((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0];
                
        }
        
        
    //泛型的實(shí)際類(lèi)型參數(shù)的類(lèi)全名
        public String getEntityName(){
            
            
    return entityClass.getName();
        }
        
        
    //泛型的實(shí)際類(lèi)型參數(shù)的類(lèi)名
        public String getEntitySimpleName(){
            
            
    return entityClass.getSimpleName();
        }

        
    //泛型的實(shí)際類(lèi)型參數(shù)的Class
        public Class<T> getEntityClass() {
            
    return entityClass;
        }
        
    }

    (Class<T>)((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0];   相當(dāng)于:


        
    //代碼塊,也可將其放置到構(gòu)造子中
        {
            
    //entityClass =(Class<T>)((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0];
            try {
                Class
    <?> clazz = getClass(); //獲取實(shí)際運(yùn)行的類(lèi)的 Class
                Type type = clazz.getGenericSuperclass(); //獲取實(shí)際運(yùn)行的類(lèi)的直接超類(lèi)的泛型類(lèi)型
                if(type instanceof ParameterizedType){ //如果該泛型類(lèi)型是參數(shù)化類(lèi)型
                    Type[] parameterizedType = ((ParameterizedType)type).getActualTypeArguments();//獲取泛型類(lèi)型的實(shí)際類(lèi)型參數(shù)集
                    entityClass = (Class<T>) parameterizedType[0]; //取出第一個(gè)(下標(biāo)為0)參數(shù)的值
                }
            } 
    catch (Exception e) {
                e.printStackTrace();
            }
                
        }
        

    注意,獲取 Class 實(shí)例的時(shí)候是用 getClass(),而不是用 Base.class,獲取 Class 的方式有三種,這是其中的兩種,還有一種是 Class.forName("類(lèi)全名"),如需了解反射的基礎(chǔ)知識(shí)

    請(qǐng)前往上一篇隨筆 java 反射基礎(chǔ)

    那么,Base.class 與 getClass(),這兩個(gè)方法來(lái)獲取類(lèi)的字節(jié)碼的時(shí)候,Base.class 是寫(xiě)死了的,它得到的永遠(yuǎn)是 Base 類(lèi)的字節(jié)碼,

    而 getClass() 方法則不同,在上面代碼注釋中的第一、二行注釋我用了“實(shí)際運(yùn)行的類(lèi)”6個(gè)字,這幾個(gè)字很重要,是一定要理解的。

    為了方便大家的理解,下面插加一個(gè)小例子來(lái)加以說(shuō)明 類(lèi).class 與 getClass() 兩種方法來(lái)獲取類(lèi)的字節(jié)碼有什么區(qū)別:


    package test;
    /**
     * -----------------------------------------
     * @描述  超類(lèi)
     * @作者  fancy
     * @郵箱  fancydeepin@yeah.net
     * @日期  2012-8-25 <p>
     * -----------------------------------------
     
    */
    public class Father {

        
    public Father (){
            
            System.out.println(
    "Father 類(lèi)的構(gòu)造子:");
            System.out.println(
    "Father.class :" + Father.class);
            System.out.println(
    "getClass()      :" + getClass());
        }
        
    }


    package test;

    /**
     * -----------------------------------------
     * @描述  超類(lèi)的子類(lèi)(超類(lèi)的實(shí)現(xiàn)類(lèi))
     * @作者  fancy
     * @郵箱  fancydeepin@yeah.net
     * @日期  2012-8-25 <p>
     * -----------------------------------------
     
    */
    public class Children extends Father{

        
    }


    package test;
    /**
     * -----------------------------------------
     * @描述  測(cè)試類(lèi)
     * @作者  fancy
     * @郵箱  fancydeepin@yeah.net
     * @日期  2012-8-25 <p>
     * -----------------------------------------
     
    */
    public class Test {

        
    public static void main(String[] args){
            
            
    new Children(); //實(shí)際運(yùn)行的類(lèi)是Children(Father類(lèi)的子類(lèi)或者說(shuō)是實(shí)現(xiàn)類(lèi))
        }
        
    }

    后臺(tái)打印輸出的結(jié)果:

    Father 類(lèi)的構(gòu)造子:
    Father.
    class :class test.Father
    getClass()      :
    class test.Children

     從打印出的結(jié)果看來(lái),類(lèi).class 與 getClass() 的區(qū)別很明了了,getClass() 獲取的是實(shí)際運(yùn)行的類(lèi)的字節(jié)碼,它不一定是當(dāng)前類(lèi)的 Class,有可能是當(dāng)前類(lèi)的子類(lèi)的 Class,具體是哪

    個(gè)類(lèi)的 Class,需要根據(jù)實(shí)際運(yùn)行的類(lèi)來(lái)確定,new 哪個(gè)類(lèi),getClass() 獲取的就是哪個(gè)類(lèi)的 Class,而 類(lèi).class 獲取得到的 Class 永遠(yuǎn)只能是該類(lèi)的 Class,這點(diǎn)是有很大的區(qū)別的。

    如果 getClass() 理解了,那 clazz.getGenericSuperclass() 也應(yīng)該能夠理解了的,千萬(wàn)不要以為 clazz.getGenericSuperclass() 獲取得到的是 Object 類(lèi)那就成了,

    實(shí)際上假如當(dāng)前運(yùn)行的類(lèi)是 Base 類(lèi)的子類(lèi),那么 clazz.getGenericSuperclass() 獲取得到的就是 Base 類(lèi)。

    (Class<T>) parameterizedType[0],怎么就知道第一個(gè)參數(shù)(parameterizedType[0])就是該泛型的實(shí)際類(lèi)型呢?很簡(jiǎn)單,因?yàn)?Base<T> 的泛型的類(lèi)型

    參數(shù)列表中只有一個(gè)參數(shù),所以,第一個(gè)元素就是泛型 T 的實(shí)際參數(shù)類(lèi)型。


    package test;
    /**
     * -----------------------------------------
     * @描述  測(cè)試類(lèi)
     * @作者  fancy
     * @郵箱  fancydeepin@yeah.net
     * @日期  2012-8-25 <p>
     * -----------------------------------------
     
    */
    public class Test {

        
    public static void main(String[] args){
            
            Base
    <String> base = new Base<String>();
            System.out.println(base.getEntityClass());                        
    //打印輸出 null
        
    //    System.out.println(base.getEntityName());                //拋出 NullPointerException 異常
        
    //    System.out.println(base.getEntitySimpleName()); //拋出 NullPointerException 異常
        }
        
    }

    從打印的結(jié)果來(lái)看,Base 類(lèi)并不能直接來(lái)使用,為什么會(huì)這樣?原因很簡(jiǎn)單,由于 Base 類(lèi)中的 clazz.getGenericSuperclass() 方法,如果隨隨便便的就確定 Base 類(lèi)的泛型的類(lèi)型

    參數(shù),則很可能無(wú)法通過(guò) Base 類(lèi)中的 if 判斷,導(dǎo)致 entityClass 的值為 null,像這里的 Base<String>,String 的 超類(lèi)是 Object,而 Object 并不能通過(guò) if 的判斷語(yǔ)句。

    Base 類(lèi)不能夠直接來(lái)使用,而是應(yīng)該通過(guò)其子類(lèi)來(lái)使用,Base 應(yīng)該用來(lái)作為一個(gè)基類(lèi),我們要用的是它的具體的子類(lèi),下面來(lái)看下代碼,它的子類(lèi)也不是隨便寫(xiě)的:


    package test;
    /**
     * -----------------------------------------
     * @描述  Base類(lèi)的實(shí)現(xiàn)類(lèi)
     * @作者  fancy
     * @郵箱  fancydeepin@yeah.net
     * @日期  2012-8-25 <p>
     * -----------------------------------------
     
    */
    public class Child extends Base<Child>{

    }

    從上面代碼來(lái)看,Base 的泛型類(lèi)型參數(shù)就是 Base 的子類(lèi)本身,這樣一來(lái),當(dāng)使用 Base 類(lèi)的子類(lèi) Child 類(lèi)時(shí),Base 類(lèi)就能準(zhǔn)確的獲取到當(dāng)前實(shí)際運(yùn)行的類(lèi)的 Class,來(lái)看下怎么使用


    package test;
    /**
     * -----------------------------------------
     * @描述  測(cè)試類(lèi)
     * @作者  fancy
     * @郵箱  fancydeepin@yeah.net
     * @日期  2012-8-25 <p>
     * -----------------------------------------
     
    */
    public class Test {

        
    public static void main(String[] args){
            
            Child child 
    = new Child();
            System.out.println(child.getEntityClass());
            System.out.println(child.getEntityName());
            System.out.println(child.getEntitySimpleName());
        }
        
    }

    后臺(tái)打印輸出的結(jié)果:


    class test.Child
    test.Child
    Child

    好了,文章接近尾聲了,如果你能理解透這個(gè)例子,你可以將這個(gè)思想運(yùn)用到 DAO 層面上來(lái),以 Base 類(lèi)作為所有 DAO 實(shí)現(xiàn)類(lèi)的基類(lèi),在 Base 類(lèi)里面實(shí)現(xiàn)數(shù)據(jù)庫(kù)的 CURD 等基本

    操作,然后再使所有具體的 DAO 類(lèi)來(lái)實(shí)現(xiàn)這個(gè)基類(lèi),那么,實(shí)現(xiàn)這個(gè)基類(lèi)的所有的具體的 DAO 都不必再實(shí)現(xiàn)數(shù)據(jù)庫(kù)的 CURD 等基本操作了,這無(wú)疑是一個(gè)很棒的做法。


    (通過(guò)反射獲得泛型的實(shí)際類(lèi)型參數(shù))補(bǔ)充:

    泛型反射的關(guān)鍵是獲取 ParameterizedType 接口,再調(diào)用 ParameterizedType 接口中的 getActualTypeArguments() 方法就可獲得實(shí)際綁定的類(lèi)型。

    由于去參數(shù)化(擦拭法),也只有在 超類(lèi)(調(diào)用 getGenericSuperclass 方法) 或者成員變量(調(diào)用 getGenericType 方法)或者方法(調(diào)用 getGenericParameterTypes 方法)

    像這些有方法返回 ParameterizedType 類(lèi)型的時(shí)候才能反射成功。

    上面只談到超類(lèi)如何反射,下面將變量和方法的兩種反射補(bǔ)上:

    通過(guò)方法,反射獲得泛型的實(shí)際類(lèi)型參數(shù):

    package test;

    import java.lang.reflect.Method;
    import java.lang.reflect.ParameterizedType;
    import java.lang.reflect.Type;
    import java.util.Collection;

    /**
     * -----------------------------------------
     * @描述  測(cè)試類(lèi)
     * @作者  fancy
     * @郵箱  fancydeepin@yeah.net
     * @日期  2012-8-26 <p>
     * -----------------------------------------
     
    */
    public class Test {

        
    public static void main(String[] args){
            
    /**
             * 泛型編譯后會(huì)去參數(shù)化(擦拭法),因此無(wú)法直接用反射獲取泛型的參數(shù)類(lèi)型
             * 可以把泛型用做一個(gè)方法的參數(shù)類(lèi)型,方法可以保留參數(shù)的相關(guān)信息,這樣就可以用反射先獲取方法的信息
             * 然后再進(jìn)一步獲取泛型參數(shù)的相關(guān)信息,這樣就得到了泛型的實(shí)際參數(shù)類(lèi)型
             
    */
            
    try {
                Class
    <?> clazz = Test.class//取得 Class
                Method method = clazz.getDeclaredMethod("applyCollection", Collection.class); //取得方法
                Type[] type = method.getGenericParameterTypes(); //取得泛型類(lèi)型參數(shù)集
                ParameterizedType ptype = (ParameterizedType)type[0];//將其轉(zhuǎn)成參數(shù)化類(lèi)型,因?yàn)樵诜椒ㄖ蟹盒褪菂?shù),且Number是第一個(gè)類(lèi)型參數(shù)
                type = ptype.getActualTypeArguments(); //取得參數(shù)的實(shí)際類(lèi)型
                System.out.println(type[0]); //取出第一個(gè)元素
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        
    //聲明一個(gè)空的方法,并將泛型用做為方法的參數(shù)類(lèi)型
        public void applyCollection(Collection<Number> collection){
            
        }
    }

    后臺(tái)打印輸出的結(jié)果:


    class java.lang.Number


    通過(guò)字段變量,反射獲得泛型的實(shí)際類(lèi)型參數(shù):

    package test;

    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.lang.reflect.ParameterizedType;
    import java.lang.reflect.Type;
    import java.util.Collection;
    import java.util.Map;

    /**
     * -----------------------------------------
     * @描述  測(cè)試類(lèi)
     * @作者  fancy
     * @郵箱  fancydeepin@yeah.net
     * @日期  2012-8-26 <p>
     * -----------------------------------------
     
    */
    public class Test {

        
    private Map<String, Number> collection;
        
        
    public static void main(String[] args){
            
            
    try {
                
                Class
    <?> clazz = Test.class//取得 Class
                Field field = clazz.getDeclaredField("collection"); //取得字段變量
                Type type = field.getGenericType(); //取得泛型的類(lèi)型
                ParameterizedType ptype = (ParameterizedType)type; //轉(zhuǎn)成參數(shù)化類(lèi)型
                System.out.println(ptype.getActualTypeArguments()[0]); //取出第一個(gè)參數(shù)的實(shí)際類(lèi)型
                System.out.println(ptype.getActualTypeArguments()[1]); //取出第二個(gè)參數(shù)的實(shí)際類(lèi)型
                
            } 
    catch (Exception e) {
                e.printStackTrace();
            }
        }
        
    }

    后臺(tái)打印輸出的結(jié)果:


    class java.lang.String
    class java.lang.Number




       [ 轉(zhuǎn)載出處:http://www.tkk7.com/fancydeepin ]


     

    posted on 2013-12-31 13:45 午后星期午 閱讀(109) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): Java

    主站蜘蛛池模板: 99久久国产免费中文无字幕| 国产免费牲交视频免费播放| 国产亚洲精品国产福利在线观看| 美女露100%胸无遮挡免费观看| 国产V片在线播放免费无码| 日本视频免费高清一本18| 4hu四虎最新免费地址| 韩国18福利视频免费观看| 亚洲偷自拍拍综合网| 99人中文字幕亚洲区| 亚洲人成色777777精品| 一级毛片免费全部播放| 久久久久免费看成人影片| 免费看少妇作爱视频| 在线a亚洲v天堂网2019无码| 亚洲视频在线免费看| 色欲aⅴ亚洲情无码AV| 国产在线国偷精品免费看| 永久免费av无码不卡在线观看| 五月婷婷亚洲综合| 亚洲AV本道一区二区三区四区| 亚洲最大av资源站无码av网址| 新最免费影视大全在线播放| 24小时日本电影免费看| mm1313亚洲精品国产| 久久亚洲AV成人无码国产| 国产成人不卡亚洲精品91| 美丽姑娘免费观看在线观看中文版 | 亚洲一区二区三区亚瑟| 日韩免费码中文在线观看| 久久久久免费看成人影片| mm1313亚洲国产精品美女| 亚洲免费在线观看视频| 日韩在线视频播放免费视频完整版 | 亚洲AV无码专区在线观看成人| 少妇性饥渴无码A区免费| 午夜神器成在线人成在线人免费| 日本亚洲视频在线| 色婷婷亚洲一区二区三区| 91九色老熟女免费资源站| 国产精品亚洲二区在线观看|