JDK 1.5引入的新特性 -- 泛型

          1. 引言

          JDK 1.5 中引入了新的語言成分, 泛型(Generics)是其中較為重要的一個.
          簡單的泛型(Defining Simple Generics)

          以下代碼摘自java.util包的List接口和Iterator接口的定義:

          public interface List<E> {
          ? void add(E x);
          ? Iterator<E> iterator();
          }
          public interface Iterator<E> {
          ? E next();
          ? boolean hasNext();
          }

          類型參數

          與尖括號有關的一些東西是JDK 5引入的新東西, 它們是List和Iterator接口的"形式的類型參數"(簡稱"類型形參")聲明.
          而在對泛型聲明List進行調用時(例如: List<Integer>), 所有出現的類型形參(如
          E)的地方, 都會被"實際的類型參數"(簡稱"類型實參", 如 Integer)所替換掉.

          雖然與C++中的模板機制在形式上很想像, 但必需注意, Java中的泛型聲明決不會在調用時被展成多份副本: 不論是在源碼級, 二進制級, 還是在磁盤或內存中, 都不會被展開!

          泛型聲明只會也只需編譯一次, 并生成一個類文件(class文件), 這一點跟普通的類或接口完全一樣.

          類型參數其實跟方法或構造器中所用的通常參數相類似. 一個方法中可以聲明它用以處理的"形式的值參數", 相似地, 泛型聲明也有其"形式的類型參數"; 當方法被調用時, 實際參數會替換形式參數, 然后執行方法體, 同樣, 當泛型聲明被調用時, 實際的類型參數會替換掉形式的類型參數.

          關于命名約定的備注: 推薦使用精煉而簡明(如, 單個字符)的方式為形式的類型參數命名. 最好避免使用小寫字符, 以便與普通的類或接口的參數相區分開來. 許多宣傳品類型使用 E 表示其元素的類型形參.


          2. 泛類型與子類型化(Generics and Subtyping)

          先看以下兩行代碼是否合法:
          List<String> ls = new ArrayList<String>(); // 1
          List<Object> lo = ls; // 2
          第一行沒問題, 關鍵在第二行代碼, 大多數人會認為, "一個String的List自然更是一個Object的List", 因此, 第2行沒問題.

          好, 接著看以下代碼:
          lo.add(new Object()); // 3
          String s = ls.get(0); // 4: 試圖將一個Object賦給一個String!

          可見, 通過別名lo, 我們能對ls, 一個String的列表, 進行數據操作(特別是插入一個Object), 從而導致ls不僅僅是容納了String對象! 這是Java編譯器不容許的! 編譯時, 第2行會報告一個編譯錯誤的.

          通常, 若Foo是Bar的一個子類型(子類或子接口), G是某個泛型聲明, 則G<Foo>并不是G<Bar>的一個子類型.

          這一點往往是最難以理解的, 因為它和通常的直觀相左. 在直觀的理解中, 我們實際上假定了集合是不會變動的, 但java語言中則非如此.


          3. 通配(Wildcards)

          假定要輸出一個集合中的所有元素. 以下分別是舊版本及新版本(JDK 1.5)中的寫法:

          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<Object>. 而正如上節看到的, Collection<Object>并不是其它各種集合的超類型(父類型).

          所有集合的超類型應該寫作: Collection<?>, 讀作: collection of unknown(未知集合), 即一個集合, 其元素類型可以與任何類型相匹配. 因此稱這種類型為"通配類型".

          正確實現上述舊版本的代碼可以這么寫:
          void printCollection(Collection<?> c) {
          ? for(Object e : c) {
          ??? System.out.println(e);
          }}

          這時, 可以用任意類型的集合來調用此方法. 注意在方法體中, 仍然從 c 中讀入元素并賦給了Object, 這是沒有錯誤的, 因此不論類型實參是何種集合, 它的元素都是object. 然而, 如果任意給它增加一個object則是不安全的:

          Collection<?> c = new ArrayList<String>();
          c.add(new Object()); // 編譯時的錯誤

          由于我們不知道c的元素類型是什么, 所以不能給它增加一個object. 方法add()接受一個類型E的參數, 而E與集合的元素類型相同. 當類型實參是?時, 它表示"未知的類型", 我們傳遞給add的參數必須是這個"未知類型"的子類型. 不幸的是, 既然類型未知, 也就無法決定其子類型, 于是什么也不能作為其參數. 唯一的例外是null, 因為null是所有類型的一個成員.

          另一方面, 如果給了一個List<?>, 我們可以調用get()方法并使用其返回的元素. 雖然返回的元素類型是"未知類型", 但它總歸是一個object, 因此將get()返回的元素賦給一個Object類型的變量, 或將其傳遞給一個可接受Object的參數都是安全的.

          posted on 2006-11-20 15:44 Timothy 閱讀(336) 評論(0)  編輯  收藏 所屬分類: JAVA大本營

          <2025年5月>
          27282930123
          45678910
          11121314151617
          18192021222324
          25262728293031
          1234567

          導航

          統計

          公告

          語義Web : 網絡就是計算機,軟件就是服務!

          常用鏈接

          留言簿(4)

          隨筆檔案(20)

          文章分類(25)

          文章檔案(25)

          新聞分類(4)

          新聞檔案(4)

          相冊

          收藏夾(27)

          個人主頁

          網絡博客

          最新隨筆

          搜索

          積分與排名

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 阿拉尔市| 紫金县| 宁海县| 临海市| 平乡县| 枝江市| 富民县| 两当县| 子长县| 汉中市| 西畴县| 东方市| 宁德市| 汉川市| 冀州市| 城口县| 安泽县| 三原县| 双城市| 乌什县| 庆元县| 久治县| 闽侯县| 吕梁市| 灵宝市| 沙田区| 宜兴市| 阆中市| 赫章县| 民勤县| 赞皇县| 安丘市| 信宜市| 曲靖市| 华坪县| 武冈市| 仁化县| 原阳县| 利辛县| 霍林郭勒市| 佛山市|