幻境
          We are extremely fortunate not to know precisely the kind of world we live in
          posts - 22,comments - 39,trackbacks - 0

          簡介

          泛型其實并不是一種新的語言元素,C++中早就就有,但是在C++之后的java卻沒有吸收這個特性,現在Java也有了泛型的特性,大概也和.Net的競爭有關系吧。

          首先看泛型的一個應用。

          在過去,我們可能經常要寫一些類似這樣的代碼:

          List stringList=new LinkedList();

          stringList.add("firstString");

          stringList.add("secondString");

          String str=(String)stringList.iterator().next();

           

          實際上第三行對String的類型轉換意義并不大,因為通常我們如果在操作一個List,都是知道這個List里面放的是什么類型對象的,但是我們如果不這樣寫又通不過語法檢查。

          利用java的泛型機制,我們可以這么寫:

          List<String> stringList=new LinkedList<String>();

          stringList.add("firstString");

          stringList.add("secondString");

          String str=stringList.iterator().next();

           

          這樣做的好處是在定義容器的時候就指明了容器中的類型,一方面我們不再需要取一個元素時候做強制類型轉換,另外一方面如果在這個容器中放入的對象類型不符合要求,那么會在編譯時候產生一個錯誤,而不是在運行時候才拋出一個異常。

          另外這樣也提高了程序的可讀性。

          泛型類型的定義

          下面是一個簡單的使用泛型類的定義:

          public class MyGenericClass<T> {

              private T value;

           

              public T getValue() {

                 return value;

              }

             

              public void setValue(T value) {

                 this.value = value;

              }  

          }

           

          值得注意的一點是,靜態變量不能夠使用泛型定義,也就是說類似下面的語句是非法的:

           

          public class MyGenericClass<T> {

              public static T value;//錯誤的定義

          }

           

           

          此外,泛型的定義不會被繼承,舉個例子來說,如果AB的子類,而C是一個聲明了泛型定義的類型的話,C<A>不是C<B>的子類。為了更好的說明,可以看下面的代碼,這段代碼是錯誤的。

          List<String> strList =new ArrayList<String>();

          List<Object> objList=strList;  //錯誤的賦值

           

          不過這樣一段代碼是正確的:

          List<Object> strList =new ArrayList<Object>();

          strList.add("a string");

           

          統配類型

          假設我們需要這樣一個函數,使用它可以把一個集合中所有的元素打印出來,在以前我們可能這樣定義:

          void printCollection(Collection c) {

              Iterator i = c.iterator();

              for (k = 0; k < c.size(); k++)

              {

                 System.out.println(i.next());

              }

          }

           

          使用新的泛型特性我們可以這樣寫:

          void printCollection(Collection<Object> c)

          {

              for (Object e : c)

              {

                 System.out.println(e);

              }

          }

           

          但是這樣有一個問題,假如我們現在有個對象類型是Collection<String>,那么我們不能夠將它作為參數傳給printCollection,因為Collection<String>并不是Collection<Object>的子類。

          為了解決這個問題,我們可以使用統配類型?,也就是定義成下面這個樣子:

          void printCollection(Collection<?> c)

          {

              for (Object e : c)

              {

                 System.out.println(e);

              }

          }

           

          可以說Collection<?>是所有Collection的父類。

          再來看一段下面的代碼

          private void clearAllMaps(Collection<Map> c)

          {

                 for(Map m:c)

                 {

                     m.clear();

                 }

          }

           

          毫無疑問,它也存在上面我們所說的問題,也就是對HashMap之類Map的子類無法進行操作,但是如果我們將參數改成Collection<?>又不大合理,因為我們只希望對父類為Map的子類進行操作,那么我們可以這樣改寫:

          private void clearAllMaps(Collection<? extends Map> c)

          {

              for(Map m:c)

              {

                 m.clear();

              }

          }

           

          類似于? extends Map之類的統配符稱為限定統配類型。

          假設一個對象h類型為Collection<HashMap>,那么我們將h作為參數傳給clearAllMaps,如下面一段代碼所示:

          List<HashMap<String,String>> h=new ArrayList<HashMap<String,String>>();

          HashMap<String,String> m=new HashMap<String,String>();

          m.put("key","value");

          h.add(m);

          clearAllMaps(h);

           

          對于在類似于上面所說,使用了? extend XXX的方法,值得注意的一點是不能夠在方法體內用XXX的子類對象作為代替。如下面一段代碼是錯誤的:

          public void addRectangle(List<? extends Shape> shapes)

          {

              shapes.add(0, new Rectangle()); // 錯誤用法!

          }

           

          這里我們假設RectangleShape的一個子類。

          不允許這樣寫的原因比較簡單,因為調用該方法時候參數類型可能是Shape的另外一個子類。假如說Shape除了Rectangle這個子類以外還有另外一個子類Circle,那么我們可以把一個List<Circle>類型的對象作為參數傳給這個方法(注意這樣是合法的),而在方法體內卻把一個Rectangle對象放到了shapes里面,這顯然是不合理的。

          除了extends,在泛型參數類型中還可以使用super關鍵字,參照下面一段程序:

          private void addString(Collection <? super String> c)

          {

                 c.add("a String");

          }

           

          泛型函數

          我們在前面提到了統配類型,現在讓我們來設想一個函數,它實現這樣的功能,將一個數組中的元素添加到一個Collection中,為了保證程序的通用性,我們可能會寫出另外一段錯誤的代碼:

          private void fromArrayToCollection(Object[] a, Collection<?> c)

          {

              for (Object o : a)

              {

                 c.add(o); // 錯誤的代碼

              }

          }

           

          那么這個函數應該怎么寫呢?我們可以通過對函數添加泛型參數的方法實現,如下面所示:

          private <T> void  exfromArrayToCollection(T[] a,  Collection<T> c)

          {

                 for (T o : a)

                 {

                     c.add(o); //這樣是正確的

                 }

          }

           

          那么,在什么時候我們應該使用統配類型,什么時候我們應該使用泛型函數呢?答案是取決于函數參數之間,函數參數和返回值之間的類型依賴性。

          如果一個函數的參數類型與函數返回的參數沒有必然關聯,同時對于該函數其他的參數的類型也沒有依賴關系,那么我們就應該使用統配符,否則就應該使用泛型函數。

          為了更清楚地說明這一點,我們可以看一下java.util包中Collections類型幾個方法的定義:

          class Collections {

              static void swap(List<?> list, int i, int j) {...}

              static <T> void  copy                                 (List<? super T> dest, List<? extends T> src)    {...}

          }

           

          其中swap函數實際上也可以這樣定義:

          static <T>void swap(List<T> list, int i, int j) {...}

           

          但是注意到這里泛型類型參數T只在參數中用到了一次,也就是說它和函數其他部分沒有依賴性,這可以看作是我們應該使用?的一個標志。

          copy方法中,拷貝源src中的元素必須是dest所能夠接受的,src中的元素必須是T的一個子類,但是具體它是哪種子類我們又不必關心,所以方法中使用了泛型作為一個類型參數,同時也用了統配類型作為第二類型參數

          posted on 2005-05-12 10:55 閱讀(986) 評論(0)  編輯  收藏 所屬分類: 編程相關
          主站蜘蛛池模板: 克拉玛依市| 明星| 仁布县| 通化市| 奎屯市| 玉田县| 宜宾县| 五家渠市| 海阳市| 慈利县| 青川县| 诸暨市| 邮箱| 察隅县| 于都县| 石狮市| 五峰| 武隆县| 松江区| 凤翔县| 和龙市| 藁城市| 南漳县| 安西县| 吉林省| 颍上县| 富顺县| 泸西县| 崇文区| 双辽市| 大丰市| 邓州市| 九龙坡区| 双柏县| 曲松县| 马山县| 海丰县| 崇义县| 普兰店市| 应用必备| 武定县|