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

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

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

    隨筆-67  評論-522  文章-0  trackbacks-0
        大象根據自己對泛型和反射的使用,來談談對它們的理解,順便整理一下知識,記錄下來,以便以后查找。
        至少在我看來,JDK5.0絕對是一個很具有里程碑意義的版本,在這個版本中,提供了非常多的很有價值的新特性,泛型就是其中之一,并且對反射機制進行了增強,而且5.0版本還把以前集合框架進行了重構全部添加了泛型支持。
        5.0發布到現在差不多快有10年時間了,關于這方面的知識介紹網上可以查到很多,書上也都有講到。大象現在再寫這些東西,一是將自己的經歷體會總結出來作一個積累,另外一點是希望能夠給剛接觸這方面的童鞋一點幫助。
        泛型最大的好處就是類型檢查,尤其是對集合非常有用,另外在底層代碼設計中很有用處,它實現了重用的功能。泛型有兩種定義方式,一個是泛型類,另一個是泛型方法。
        那到底什么是泛型呢?簡單點講(可能不嚴謹),就是用到了類型參數這樣的類型變量,不管是類、接口還是方法,都可以說是用到了泛型。請看例子:
        泛型類
        public class Person<T> {
            private T t;
            public T getT() {
                return t;
            }
            public void setT(T t) {
                this.t = t;
            }
        }
        T就是類型變量,是一個參數化類型,用尖括號(<>)括起來,放在類名的后面。泛型類的類型變量可以定義多個。類型變量一般都使用一個大寫字母表示,比如本例的Person<T>JDK中的List<E>Map<K,V>等等。
        用具體的類替換類型變量就可以實例化泛型類:Person<Man> person = new Person<Man>();
        像這樣實例化是錯誤的:Person<T> person = new Person<T>(); //ERROR
        泛型方法

    public <T> T get(String key, Object params) {
           return (T) getSqlSession().selectOne(key, params);
        }

        這是我在SSM3示例的MyBatisDao這個類里面定義的一個方法,此方法就是一個泛型方法。<T>就是類型變量,而get前面的T是返回類型。其實這個方法是存在類型安全問題的,如果我在RoleService里面調用這個方法,將返回類型T寫成User,編譯器是不會有任何警告信息的。
        但如果我改寫一下,將MyBatisDao加上泛型,public class MyBatisDao<T> extends SqlSessionDaoSupport
        這時User的返回類型就出現編譯錯誤了:
        
        編譯器根據MyBatisDao<Role>這個Role類型變量就會推斷出它里面定義的get方法應該返回Role類型。不過這樣改過之后MyBatisDao就變成泛型類了,而get方法也不再是泛型方法。那么泛型方法能不能有安全檢查呢?有,但是需要一些編程技巧,關鍵還是跟你寫的泛型方法有關系,后面提到的類型參數的限定可以對泛型加以約束,解決一些安全檢查的問題。
        類型參數的限定
        對于像<T>這樣的類型變量所代表的范圍有時太大了點,有時不方便使用。比如現在需要實現了java.io.Serializable接口的泛型類,那么這應該如何做呢?JDK那幫專家們為我們設計了一種叫做“有限制的通配符類型”來解決這個問題。一般我們稱為上限 下限,他們一般寫成下面這樣:
        上限:<extends Serializable>或者<? extends T>
        下限:<? super T>
        問號(?)叫做無限制的通配符,它可以表示任何類型。有時候使用類型變量不是那么的方便,通配符類型就很好的解決了這個問題。
        <T extends Serializable>的含義是,T為實現了Serializable接口的類,T為綁定類型(Serializable)的子類型,T和綁定類型可以是接口也可以是類。如果想再加個實現了Comparable接口的限定,只需要這樣寫:<T extends Serializable & Comparable>這樣寫有點不嚴謹,因為Comparable接口是一個泛型接口可以接收泛型參數,現在我們不討論這么復雜的情況。
        <? super T>可以這樣理解,任何T類型變量的超類型,還包括T本身,因為T可以看成是它本身的一個超類型。
        那為什么說extends是上限,而extends是下限呢?通過前面兩個解釋就應該可以看出來,extends Serializable或者extends T說明類型變量必須是Serializable的子類和T變量的子類型,這是不是相當于限制了類型變量的上限了?同理就可以理解下限的含義了。
        說了這么多關于上限和下限的東西,那他們到底有什么用?和怎么用呢?簡單來講,extends限定的類型參數可以從泛型對象讀取,super限定的類型參數可以向泛型對象寫入。這樣說可能有些童鞋要暈了,這到底說的神馬東西呢?
        讓我們換個方式來講,關于泛型的上限與下限已經總結出來一個公式:PECS
        PECS表示producer-extendsconsumer-super
        上面這個就是說如果參數化類型表示一個生產者,就用<? extends T>,如果它表示一個消費者,就用<? super T>。再結合上面的說明一起來理解,是不是清楚了呢?如果還不是很理解,大象再附上一小段代碼來體會下其中的區別。
        public void add(List<? extends T> list){
            for(T t : list){
               add(t);
           }
        }
        public void add(T t){};
        public void add(T t, List<? super T> list){
            list.add(t);
        }
        泛型的擦除
        泛型主要是在編譯期有效,即編譯的時候檢查類型安全,現在寫代碼一般都會用EclipseIntelliJ,這些集成開發工具都能做到即時編譯,哪里有錯馬上會出現紅色的錯誤標識。因此如果出現類型轉換錯誤,會很明顯的看到結果。但是,在程序的運行階段,JVM是不認識泛型是神馬東西的,所有有泛型的類,接口,方法都會被擦除掉泛型,變成原生類型(raw type),即Person<T>變為PersonList<? extends T> list變成List list等等。
        下面就是之前的Person類用javap反編譯后的結果,所有的類型變量T都被擦除掉了,因為T是一個無限定類型所以用Object替換。而且add方法中的<? extends T><? super T>也被去掉了。
        public class com.bolo.Person extends java.lang.Object{
           private java.lang.Object t;
           public com.bolo.Person();
           public java.lang.Object getT();
           public void setT(java.lang.Object);
           public void add(java.util.List);
           public void add(java.lang.Object);
           public void add(java.lang.Object, java.util.List);
        }
        因此針對泛型擦除這一特點,我們需要注意這樣幾點:
            1JVM里面沒有泛型,只有普通的類和方法。
            2、所有的類型參數都用限定類型或者無限定類型用Object來替代。
            3、請謹慎處理方法重載,錯誤的使用重載不會實現想要的多態。
            4、為保證類型安全,必要時請使用強制類型轉換。
        泛型的限制
        基本類型不能作為類型參數。Person<int>就是錯誤的,只能用Person<Integer>
        類型檢查只能用原始類型。if(t instanceof Person) 如果寫成if(t instanceof Person<String>)馬上會出現編譯錯誤
        不能實例化類型變量。這樣寫是錯誤的:T t = new T()
        不能實例化參數化類型的數組。Person<Integer>[] p = new Person<Integer>[5] //ERROR
        不能定義靜態實例變量和靜態方法。如果你想這樣寫:private static T a 那對不起,編譯器馬上會給你一個錯誤提示。
        其實關于泛型的限制完全可以不用講,現在編譯器都很強大,只要你這樣做了,馬上會給你顯示一個錯誤。
        最后說下泛型對于集合的用處來說是最大的,集合是一個容器,有了泛型就更方便重用。而我們使用最頻繁的集合就是List列表,還有一個容器就是數組,大象在這里強烈建議大家多用List,盡量或最好不要用數組。其一是List有類型安全性檢查,其二是數組的功能List都提供了并且更豐富,其三Listgc進行了優化。如果使用數組,特別是操作對象數組,如果經驗不足,沒有釋放數組里面的對象引用,則很容易造成內存泄漏的問題。
        以上這些都是大象使用泛型的一點經驗總結,有什么不對的,或不完善的地方,還請各位指出來,謝謝!
        本文為菠蘿大象原創,如要轉載請注明出處。http://www.tkk7.com/bolo
    posted on 2014-04-29 17:09 菠蘿大象 閱讀(7028) 評論(0)  編輯  收藏 所屬分類: Java
    主站蜘蛛池模板: 亚洲精品V欧洲精品V日韩精品| 亚洲欧洲日韩国产| 成人性生交大片免费看好| 久久亚洲AV成人无码软件 | 美女黄网站人色视频免费国产 | 久久国产亚洲高清观看| 成人午夜视频免费| 丁香花在线观看免费观看图片| 久久精品国产亚洲av麻豆色欲| 国产美女被遭强高潮免费网站| 日韩精品无码免费专区午夜不卡| 国产v亚洲v天堂a无| 国产精品亚洲玖玖玖在线观看| 亚洲免费福利视频| 深夜a级毛片免费视频| 久久久久亚洲精品无码蜜桃| 国产成人在线免费观看| 69国产精品视频免费| 成年网站免费入口在线观看| 亚洲成A∨人片在线观看无码| 亚洲一级片免费看| 欧美a级成人网站免费| 久久精品成人免费网站| 国产成人亚洲精品电影| 亚洲综合激情另类小说区| 亚洲精品无码久久久| 免费无码A片一区二三区| 久9久9精品免费观看| 免费人成网站永久| 亚洲人成www在线播放| 久久亚洲国产视频| 亚洲精品国产福利一二区| 我要看免费的毛片| 猫咪免费人成网站在线观看| 成人免费乱码大片A毛片| 久久亚洲精品高潮综合色a片| 亚洲国产中文在线二区三区免| 亚洲精品高清国产一线久久| 亚洲第一区在线观看| 日本免费一本天堂在线| 国产卡二卡三卡四卡免费网址|