探秘 Java 中的泛型(Generic)

本文包括:

  1. JDK5之前集合對(duì)象使用問(wèn)題
  2. 泛型的出現(xiàn)
  3. 泛型應(yīng)用
  4. 泛型典型應(yīng)用
  5. 自定義泛型——泛型方法
  6. 自定義泛型——泛型類(lèi)
  7. 泛型的高級(jí)應(yīng)用——通配符(wildcard)
  8. 泛型通配符的擴(kuò)展閱讀
Paste_Image.png

泛型(Generic)

1、JDK5之前集合對(duì)象使用問(wèn)題

  1. 可以向集合添加任何類(lèi)型對(duì)象

  2. 從集合取出對(duì)象時(shí),數(shù)據(jù)類(lèi)型丟失杭措,使用與類(lèi)型相關(guān)方法搂鲫,強(qiáng)制類(lèi)型轉(zhuǎn)換。

  3. 程序存在安全隱患

2同窘、泛型的出現(xiàn)

  1. JDK5中的泛型允許程序員使用泛型技術(shù)限制集合的處理類(lèi)型

     List<String> list = new ArrayList<String>();
    
  2. 注意:泛型是提供給javac編譯器使用的,它用于限定集合的輸入類(lèi)型,讓編譯器在源代碼級(jí)別上晓殊,即擋住向集合中插入非法數(shù)據(jù)。但編譯器編譯完帶有泛型的java程序后伤提,生成的.class文件中將不再帶有泛型信息巫俺,因此程序運(yùn)行效率不受影響,這個(gè)過(guò)程稱(chēng)為“擦除”肿男。

  3. 泛型的基本術(shù)語(yǔ)介汹,以ArrayList<E>為例:"<>"讀作typeof

    • ArrayList<E>中的E稱(chēng)為類(lèi)型參數(shù)變量
    • ArrayList<Integer>中的Integer稱(chēng)為實(shí)際類(lèi)型參數(shù)。
    • 整個(gè)ArrayList<Integer>稱(chēng)為參數(shù)化類(lèi)型ParameterizedType

3舶沛、 泛型應(yīng)用

  • 類(lèi)型安全檢查

  • 編寫(xiě)通用Java程序(Java框架)

4嘹承、泛型典型應(yīng)用

  1. 使用Type-Safe的集合對(duì)象

    • List

    • Set

    • Map

  2. List示例:

     //使用類(lèi)型安全List
     List<String> list = new LinkedList<String>();
     //因?yàn)槭褂梅盒停荒芴砑覵tring類(lèi)型元素
     list.add("aaa");
     list.add("bbb");
     list.add("ccc");
     
     //遍歷List有三種方法
     
     //方法一:因?yàn)長(zhǎng)ist是有序的(存入順序和取出順序一樣)如庭,通過(guò)size和get方法進(jìn)行遍歷
     for (int i = 0; i < list.size(); i++) {
         String s = list.get(i);
         System.out.println(s);
     }
    
     //方法二:因?yàn)長(zhǎng)ist繼承Collection接口叹卷,通過(guò)Collection的iterator進(jìn)行遍歷
     Iterator<String> iterator = list.iterator();
     //遍歷iterator通過(guò)迭代器hasNext和next方法進(jìn)行遍歷
     while (iterator.hasNext()) {
         String s = iterator.next();
         System.out.println(s);
     }
    
     //方法三:JDK5引入了foreach循環(huán)結(jié)構(gòu),通過(guò)foreach結(jié)構(gòu)遍歷List
     for (String s : list) {
         System.out.println(s);
     }
    
  3. Set示例:

     //使用類(lèi)型安全Set
     Set<String> set = new TreeSet<String>();
    
     set.add("asd");
     set.add("fdf");
     set.add("bxc");
    
     //取出Set元素有兩種方法坪它,因?yàn)镾et是無(wú)序的骤竹,所以比List少一種遍歷方法
     //方法一:Set繼承Collection,所以可以使用Iterator遍歷
     Iterator<String> iterator = set.iterator();
     while (iterator.hasNext()) {
         String s = iterator.next();
         System.out.println(s);
     }
    
     //方法二:JDK5引入了foreach
     for (String s : set) {
         System.out.println(s);
     }
    
  4. Map示例:

     //使用類(lèi)型安全的Map -- 因?yàn)镸ap是一個(gè)鍵值對(duì)結(jié)構(gòu)往毡,執(zhí)行兩個(gè)類(lèi)型泛型
     Map<String, String> map = new HashMap<String, String>();
    
     map.put("aaa", "111");
     map.put("bbb", "222");
    
     //取出Map元素有兩種方法
     //方法一:通過(guò)Map的keySet()進(jìn)行遍歷
     Set<String> keys = map.keySet(); // 獲得key的集合
     for (String key : keys) {
         System.out.println(key + ":" + map.get(key));
     }
    
     //方法二:通過(guò)map的entrySet()蒙揣,獲得每一個(gè)鍵值對(duì)。
     Set<Map.Entry<String, String>> entrySet = map.entrySet(); //每個(gè)元素都是一個(gè)鍵值對(duì)
    
     for (Entry<String, String> entry : entrySet) {
         //通過(guò)entry的getKey()和getValue()獲得每一個(gè)鍵值對(duì)的鍵和值
         System.out.println(entry.getKey() + ":" + entry.getValue());
     }
    

5开瞭、自定義泛型——泛型方法

  1. Java中的普通方法懒震、構(gòu)造方法和靜態(tài)方法中都可以使用泛型罩息。方法使用泛型前,必須對(duì)泛型進(jìn)行聲明个扰,語(yǔ)法:<T>瓷炮,T可以是任意字母,但通常必須要大寫(xiě)递宅。<T>通常需放在方法的返回值聲明之前娘香。
    例如:

     public static <T> void doxx(T t);
    
  2. 假設(shè)有這樣一個(gè)需求,要求實(shí)現(xiàn)指定位置上數(shù)組元素的交換恐锣,這個(gè)數(shù)組中的元素可能是int型茅主,可能是String類(lèi)型。

    • 未使用泛型代碼如下:

        //String類(lèi)型數(shù)組
        public void changePosition(String[] arr, int index1, int index2) {
            String temp = arr[index1];
            arr[index1] = arr[index2];
            arr[index2] = temp;
        }
        
        //int類(lèi)型數(shù)組
        public void changePosition(int[] arr, int index1, int index2) {
            int temp = arr[index1];
            arr[index1] = arr[index2]; 
            arr[index2] = temp; 
        }
      
        Integer[] arr1 = new Integer[] { 1, 2, 3, 4, 5 };
        changePosition(arr1, 1, 3); 
        System.out.println(Arrays.toString(arr1));
      
        String[] arr2 = new String[] { "aaa", "bbb", "ccc", "ddd" };
        changePosition(arr2, 0, 2);
        System.out.println(Arrays.toString(arr2));
      
    • 使用泛型代碼如下:

        // 使用泛型 編寫(xiě)交換數(shù)組通用方法土榴,類(lèi)型可以String 可以 int --- 通過(guò)類(lèi)型
        public <T> void changePosition(T[] arr, int index1, int index2) {
            T temp = arr[index1];
            arr[index1] = arr[index2];
            arr[index2] = temp;
        }
      
        Integer[] arr1 = new Integer[] { 1, 2, 3, 4, 5 };
        changePosition(arr1, 1, 3); 
        System.out.println(Arrays.toString(arr1));
      
        String[] arr2 = new String[] { "aaa", "bbb", "ccc", "ddd" };
        changePosition(arr2, 0, 2);
        System.out.println(Arrays.toString(arr2));
      
    • 兩者輸出相同诀姚,所以利用泛型可以編寫(xiě)通用的Java程序

6、自定義泛型——泛型類(lèi)

  1. 如果一個(gè)類(lèi)多處都要用到同一個(gè)泛型玷禽,這時(shí)可以吧泛型定義在類(lèi)上(即類(lèi)級(jí)別的泛型)赫段,語(yǔ)法如下:

     public class GenericDao<T>{
         private T field1;
         public void save(T obj){}
         public T getId(int id){}
     }
    

    注意:靜態(tài)方法不能使用類(lèi)定義的泛型,應(yīng)該單獨(dú)定義泛型矢赁。

  2. 示例:

    如果在1.5節(jié)中還需要一個(gè)需求:倒序數(shù)組糯笙,那么可以自定義一個(gè)泛型類(lèi):

     public class ArraysUtils<A> { // 類(lèi)的泛型
         // 將數(shù)組倒序
         public void reverse(A[] arr) {
             /*
              * 只需要遍歷數(shù)組前一半元素,和后一半元素 對(duì)應(yīng)元素 交換位置
              */
             for (int i = 0; i < arr.length / 2; i++) {
                 // String first = arr[i];
                 // String second = arr[arr.length - 1 - i];
                 A temp = arr[i];
                 arr[i] = arr[arr.length - 1 - i];
                 arr[arr.length - 1 - i] = temp;
             }
         }
     
         public void changePosition(A[] arr, int index1, int index2) {
             A temp = arr[index1];
             arr[index1] = arr[index2];
             arr[index2] = temp;
         }
     }
    

對(duì)應(yīng)泛型類(lèi)型參數(shù)起名 T E K V ---- 泛型類(lèi)型可以以任意大寫(xiě)字母命名撩银,建議你使用有意義的字母
如:T Template E Element K key V value

7给涕、泛型的高級(jí)應(yīng)用——通配符(wildcard)

  1. 假設(shè)有一個(gè)方法,接受一個(gè)集合额获,并打印出集合中的所有元素够庙,如下所示:

     // ? 代表任意類(lèi)型
     public void print(List<?> list) { // 泛型類(lèi)型 可以是任何類(lèi)型 --- 泛型通配符
         for (Object string : list) {
             System.out.println(string);
         }
     }
     
     public void demo10() {
         // 打印數(shù)組中所有元素內(nèi)容
         List<String> list = new LinkedList<String>();
    
         list.add("aaa");
         list.add("bbb");
         list.add("ccc");
         print(list);
    
         List<Integer> list2 = new LinkedList<Integer>();
    
         list2.add(111);
         list2.add(222);
         list2.add(333);
    
         print(list2);
     }
    
  2. 只用通配符的情況下很少,通常還需要通過(guò)指定上下邊界抄邀,限制通配符類(lèi)型范圍耘眨。
    用法:

    • 指定上邊界:

        List<? extends Number> list = new ArrayList<Integer>(); //繼承自Number境肾,即指定了泛型的上邊界為Number剔难,且包括Number
      
    • 指定下邊界:

        List<? super String> list = new ArrayList<Object>(); //是String的父類(lèi),即指定了泛型的下邊界為String奥喻,且包括String
      
    • 上下邊界不能同時(shí)使用 :
      List<? extends Object super Integer> list = new ArrayList<Object>(); //錯(cuò)誤偶宫!沒(méi)有這么寫(xiě)的

  3. 上下邊界的應(yīng)用:

    • 范例一:

    Set中有方法:addAll(Collection<? extends E> c) //將目標(biāo)集合c的內(nèi)容添加到當(dāng)前set ,? extends E 目標(biāo)集合是E的子類(lèi)型

    即有如下代碼衫嵌,可以運(yùn)行成功:

         Set<Number> set = new HashSet<Number>();
         List<Integer> list = new ArrayList<Integer>();
         set.addAll(list); // list 中 Integer 自動(dòng)轉(zhuǎn)換為 Number
    
    • 范例二:

    TreeSet有構(gòu)造方法:TreeSet(Comparator<? super E> comparator) //傳入E的父類(lèi)型的比較器

    即有如下代碼读宙,可以運(yùn)行成功:

         Set<Apple> set = new TreeSet<Apple>(); // 默認(rèn)需要蘋(píng)果比較器排序 
         
         class FruitComparator implements Comparator<Fruit> {} //水果的比較器
         Set<Apple> set = new TreeSet<Apple>(new FruitComparator()); // 需要Apple比較器 ,傳入 Fruit比較器    楔绞,依據(jù)構(gòu)造方法结闸,可行    
    
  4. 錯(cuò)誤范例:

     public void add(List<? extends Number> list){
         list.add(100);  //會(huì)報(bào)錯(cuò)!使用通配符后酒朵,不要使用與類(lèi)型相關(guān)的方法桦锄。
     }
    

8、泛型通配符的擴(kuò)展閱讀

  1. 關(guān)于泛型還可深入研究蔫耽,在《Effective Java 2th Edition》有相關(guān)介紹结耀,感興趣的同學(xué)可以閱讀一下。

  2. 最后還介紹一下關(guān)于泛型通配符的上下邊界問(wèn)題匙铡,什么時(shí)候用上邊界图甜,什么時(shí)候用下邊界?
    PECS:producer extends consumer super

    1. 頻繁往外讀取內(nèi)容的鳖眼,適合用上界Extends黑毅。

    2. 經(jīng)常往里插入的,適合用下界Super钦讳。

  3. 例如:

     // compile error
     //    List <? extends Fruit> appList2 = new ArrayList();
     //    appList2.add(new Fruit());
     //    appList2.add(new Apple());
     //    appList2.add(new RedApple());
    
     // no error
     List <? super Fruit> appList = new ArrayList();
     appList.add(new Fruit());
     appList.add(new Apple());
     appList.add(new RedApple());
    

http://stackoverflow.com/questions/2723397/what-is-pecs-producer-extends-consumer-super

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末矿瘦,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子愿卒,更是在濱河造成了極大的恐慌缚去,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件琼开,死亡現(xiàn)場(chǎng)離奇詭異易结,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)柜候,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)搞动,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人改橘,你說(shuō)我怎么就攤上這事滋尉。” “怎么了飞主?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵狮惜,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我碌识,道長(zhǎng)碾篡,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任筏餐,我火速辦了婚禮开泽,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘魁瞪。我一直安慰自己穆律,他們只是感情好惠呼,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著峦耘,像睡著了一般剔蹋。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上辅髓,一...
    開(kāi)封第一講書(shū)人閱讀 49,046評(píng)論 1 285
  • 那天泣崩,我揣著相機(jī)與錄音,去河邊找鬼洛口。 笑死矫付,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的第焰。 我是一名探鬼主播买优,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼樟遣!你這毒婦竟也來(lái)了而叼?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤豹悬,失蹤者是張志新(化名)和其女友劉穎葵陵,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體瞻佛,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡脱篙,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了伤柄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绊困。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖适刀,靈堂內(nèi)的尸體忽然破棺而出秤朗,到底是詐尸還是另有隱情,我是刑警寧澤笔喉,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布取视,位于F島的核電站,受9級(jí)特大地震影響常挚,放射性物質(zhì)發(fā)生泄漏作谭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一奄毡、第九天 我趴在偏房一處隱蔽的房頂上張望折欠。 院中可真熱鬧,春花似錦、人聲如沸锐秦。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)农猬。三九已至赡艰,卻和暖如春售淡,著一層夾襖步出監(jiān)牢的瞬間斤葱,已是汗流浹背蛤吓。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工弹惦, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人欠拾。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓汤纸,卻偏偏與公主長(zhǎng)得像衩茸,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子贮泞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法楞慈,類(lèi)相關(guān)的語(yǔ)法,內(nèi)部類(lèi)的語(yǔ)法啃擦,繼承相關(guān)的語(yǔ)法囊蓝,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,581評(píng)論 18 399
  • (一)Java部分 1令蛉、列舉出JAVA中6個(gè)比較常用的包【天威誠(chéng)信面試題】 【參考答案】 java.lang;ja...
    獨(dú)云閱讀 7,071評(píng)論 0 62
  • 文章作者:Tyan博客:noahsnail.com 1. 什么是泛型 Java泛型(Generics)是JDK 5...
    SnailTyan閱讀 771評(píng)論 0 3
  • Java 語(yǔ)言支持的類(lèi)型分為兩類(lèi):基本類(lèi)型和引用類(lèi)型聚霜。整型(byte 1, short 2, int 4, lon...
    xiaogmail閱讀 1,345評(píng)論 0 10
  • 大概直男癌男朋友就是這樣 九點(diǎn)半不到 我說(shuō)我要睡了 他說(shuō) 好 晚安么么噠 然后繼續(xù)看他的明日之子了
    MissM_eec7閱讀 79評(píng)論 0 0