莊周夢蝶

          生活、程序、未來
             :: 首頁 ::  ::  :: 聚合  :: 管理

          Java泛型再學習

          Posted on 2007-06-05 17:07 dennis 閱讀(2696) 評論(2)  編輯  收藏 所屬分類: java
              泛型引入java語言已經有很長一段時間了,在JDK5出來的時候也非常認真地學習過,不過學習的資料都是網上泛濫并且重復的教程。這幾天下了《The Java Programming Language》的第4版,準備把jdk5引入的新東西再重新系統地學習一次,同時再次回顧下java基礎。今天記錄下學習泛型那一章的注意點。
          一、泛型類型的聲明
          1.需要著重注意的一點,比如聲明類Cell<E>:
          package net.rubyeye.javaprogramming.generic;

          public class Cell<E> {
              
          private Cell<E> next;

              
          private E element;

              
          public Cell(E element) {
                  
          this.element = element;
              }

              
          public Cell(E element, Cell<E> next) {
                  
          this.next = next;
                  
          this.element = element;
              }

              
          public E getElement() {
                  
          return element;
              }

              
          public void setElement(E element) {
                  
          this.element = element;
              }

              
          public Cell<E> getNext() {
                  
          return next;
              }

              
          public void setNext(Cell<E> next) {
                  
          this.next = next;
              }

          }

          然后如此使用:
          Cell<String> strCell = new Cell<String>("Hello");
          Cell
          <Integer> intCell = new Cell<Integer>(25);

          那么Cell<String>和Cell<Integer>是兩個類嗎?不,他們是同一個類,通過下面的實驗證明:
          assertTrue(strCell.getClass() == intCell.getClass()));

          java泛型的實現采用的“擦拭法”,Cell<E>仍然是一個類,無論E被任何具體的類型所替代。

          2.泛型的類型參數不能用于static變量、static方法和static初始化,比如下面的使用方式都不能編譯通過:
          public class Cell<E> {
              
          private static Cell<E> next;
              
              
          private static void test(E e){
                  
              }
             
          同樣,靜態方法是與類相關聯的,調用也只能通過類,假設Cell有一個靜態方法test,怎么調用才是正確的呢?
          Cell<E>.test();  //編譯錯誤
          Cell<String>.test();  //同樣編譯錯誤
          Cell.test();  //正確的方式
          類似的,泛型的類型參數不能用于聲明數組類型,比如下面的代碼同樣無法編譯通過:
          class SingleLinkQueue<E> {
              
          // 
              public E[] toArray() {
              
          //
              }
          }

          3.類型參數可以繼承其他的類和接口,如果有多個接口可以用&符號連接,通過extend參數限制了類型參數的范圍,比如:
          interface SortedCharSeqCollection<extends Comparable<E>
                                            
          & CharSequence> {
              
          //  sorted char sequence collection methods 
          }

          SortedCharSeqCollection的類型參數E強制繼承自Comparable和CharSequence接口,也就是替代的具體的類型參數必須實現這兩個接口,從而限制了類型參數(type parameter)。

          4.比較有趣的內部類的泛型,對于靜態內部類的類型參數可以與外部類的類型參數名不一樣,靜態內部類的類型參數與外部類的類型參數其實沒有一點關系,比如:
          class SingleLinkQueue<E> {
              
          static class Cell<E> {
                  
          private Cell<E> next;
                  
          private E element;
                  
          public Cell(E element) {
                      
          this.element = element;
                  }
                  
          public Cell(E element, Cell<E> next) {
                      
          this.element = element;
                      
          this.next = next;
                  }
                  
          public E getElement() {
                      
          return element;
                  }
                  
          /*  rest of Cell methods as before  */
              }

              
          protected Cell<E> head;
              
          protected Cell<E> tail;

              
          /*  rest of SingleLinkQueue methods as before  */
          }


          Cell<E>類的聲明和SingleLinkQueue<E> 兩個類中的E僅僅是名稱相同,他們之間的關聯是通過head和tail的聲明才關聯在一起,你可以將Cell<E>中的E改成F也沒關系,比如:
          package net.rubyeye.javaprogramming.generic;

          class AnotherSingleLinkQueue<E> {
              
          static class Cell<F> {
                  
          private Cell<F> next;

                  
          private F element;

                  
          public Cell(F element) {
                      
          this.element = element;
                  }

                  
          public Cell(F element, Cell<F> next) {
                      
          this.element = element;
                      
          this.next = next;
                  }

                  
          public F getElement() {
                      
          return element;
                  }
                  
          /*  rest of Cell methods as before  */
              }

              
          protected Cell<E> head;

              
          protected Cell<E> tail;

              
          /*  rest of SingleLinkQueue methods as before  */
          }

          而一般的內部類就不一樣了,內部類可以直接使用外部類的類型參數甚至隱藏。

          二、子類型與通配符
          今天讀了第2節,泛型的使用比我原先所知的更為復雜,java語法本來以簡潔優美著稱,隨著java5,java7的到來,語法是越來越復雜,甚至可以說丑陋!-_-

              要知道一點,比如List<Integer>不是List<Number>的子類,而是Collection<Integer>的子類。因為List<Integer>和List<Number>的類型是一樣的,都是List。那么如何表示參數化類型是Number的子類呢?這就需要用到通配符:
          List<? extends Number>
          表示類型變量是Number或者Number的子類。這個就是所謂的上界通配符,同樣,如果要表示類型變量是Number或者Number的super type,可以使用下界通配符:
          List<? super Number>

          而通配符List<?>等價于:
          List<? extends Object>

              通配符只能用于變量、局部變量、參數類型和返回類型,不能用于命名類和接口。比如下面的代碼將不能編譯通過:
          class MyList implements List<?>{
             
          //
          }
              通配符有另一個問題:因為通配符代表的是未知的類型,你不能在任何需要類型信息的地方使用它。比如下面的代碼同樣無法編譯通過:
          SingleLinkQueue<?> strings =
              
          new SingleLinkQueue<String>();
          strings.add(
          "Hello");               // INVALID: 無法編譯

          SingleLinkQueue
          <? extends Number> numbers =
              
          new SingleLinkQueue<Number>();
          numbers.add(Integer.valueOf(
          25));   // INVALID: 無法編譯


          三、泛型方法和類型推斷
              如果我們想參數化方法的參數和返回值的類型,這就引出了泛型方法的聲明,聲明一個泛型方法的方式如下:
          <T> T passThrough(T obj) {
              
          return obj;
          }

          這個方法限制傳入的參數的類型與返回的參數類型將一致,可以看到,在方法簽名前加上<T>即可。我們可以這樣調用這個方法:
          String s1 = "Hello";
          String s2 
          = this.<String>passThrough(s1);

          這樣的調用是不是比較奇怪?幸好提供了類型推斷,根據參數的類型來自動判斷方法的類型(比如返回值類型),因此可以直接調用:
          String s1 = "Hello";
          String s2 
          = this.passThrough(s1);

              如果方法有兩個類型變量,類型推斷將怎么處理呢?比如:
          <T> T passThrough(T obj1,T obj2) {
                  
          return (T)(obj1.toString()+obj2.toString());
              }

          然后我們傳入兩個參數,一個String,一個int,那么返回什么呢?
          String s1="test";
          String s3
          =this.passThrough(s1, 1);  //編譯出錯
          類型推斷是比較復雜的,這里將返回的將是Object類型,是傳入的參數類型的交集


          評論

          # re: Java泛型再學習  回復  更多評論   

          2011-08-04 10:37 by wangxt+
          2.泛型的類型參數不能用于static變量、static方法和static初始化,比如下面的使用方式都不能編譯通過:
          對這點很傷心

          # re: Java泛型再學習[未登錄]  回復  更多評論   

          2012-07-11 15:02 by ivy
          這種語法真的很丑陋,難道在像匯編回歸?
          主站蜘蛛池模板: 宁武县| 皮山县| 重庆市| 余姚市| 佛山市| 普兰店市| 永福县| 象山县| 临洮县| 崇仁县| 西盟| 高邮市| 贵港市| 太康县| 渝北区| 德江县| 岳池县| 比如县| 江永县| 新余市| 保靖县| 吉林省| 莎车县| 元朗区| 于田县| 荣成市| 武夷山市| 德化县| 威宁| 稻城县| 包头市| 中宁县| 邵武市| 垫江县| 文安县| 新丰县| 巫山县| 大竹县| 神农架林区| 阿拉善盟| 阳东县|