Vincent

          Vicent's blog
          隨筆 - 74, 文章 - 0, 評(píng)論 - 5, 引用 - 0
          數(shù)據(jù)加載中……

          generic-泛型/類屬

          管中窺虎

          在學(xué)習(xí) java 1.5 的過程中,我使用了 sun 公布的 tutorial ,這份文檔寫的比較詳盡易明,但是對(duì)于想快速了解 tiger 而且具有較好 java 基礎(chǔ)的人來說,大篇幅的英文文檔是比較耗時(shí)間和非必需的,所以我將會(huì)歸納這份文檔的主要內(nèi)容,在保證理解的底線上,盡力減少閱讀者需要的時(shí)間。

          ?

          在以下地址可以進(jìn)入各新增語言特色介紹以及下載相關(guān)文檔(若有)。

          http://java.sun.com/j2se/1.5.0/docs/relnotes/features.html

          ?

          第一道虎紋: generic -泛型 / 類屬

          什么是泛型

          泛型讓你在類這一層次上進(jìn)行抽象。看看例子:

          List?myIntList? = ? new ?LinkedList();? // ?1?

          myIntList.add(
          new ?Integer( 0 ));? // ?2?

          Integer?x?
          = ?(Integer)?myIntList.iterator().next();? // ?3?

          ?

          ?

          3 句的轉(zhuǎn)換類型有點(diǎn)麻煩吧~?編譯器只能保證容器類里放的是 Object 對(duì)象,要使用他們只能這樣去轉(zhuǎn)換。而且這樣的轉(zhuǎn)換也并不是完全安全的,程序員可能犯錯(cuò)誤,容器里的對(duì)象未必是他以為的對(duì)象。有沒有辦法顯式地表達(dá)出程序員的意圖,將該容器限制為只能保存特定類型的對(duì)象?這正是 generic -泛型的核心用意。

          ?

          ?

          List? < ?Integer? > ?myIntList? = ? new ?LinkedList? < ?Integer? > ?();? // ?1’?

          myIntList.add(
          new ?Integer( 0 ));? // 2’?

          Integer?x?
          = ?myIntList.iterator().next();? // ?3’?

          ?

          ?

          這樣子我們就聲明了一個(gè)只放 Integer List ,我們說 List 是一個(gè) generic Interface ,接收了一個(gè)類型參數(shù),在上例中就是 Integer 。在初始化的時(shí)候,同樣的也指定了這個(gè)類型參數(shù)。

          要注意這些工作的效果不是僅僅把原來的第 3 句的轉(zhuǎn)換工作省掉,而是由此讓編譯器確保了這個(gè) List 在程序的任何位置任何時(shí)候都用以存放正確的類型,而原來的類型轉(zhuǎn)換僅僅告訴我們?cè)谶@一單點(diǎn)處程序員自己認(rèn)為的類型。

          泛型由此為程序,尤其是大型程序,帶來了可讀性和健壯性。

          定義簡(jiǎn)單的泛型

          ?

          ?public?interface?List?<?E?>?{?

          ???????void?add(E?x);?

          ??????Iterator?
          <?E?>?iterator();?

          }
          ?public?interface?Iterator?<?E?>?{?

          ?????????E?next();?

          ?????????boolean?hasNext();?

          }
          ?

          尖括號(hào)內(nèi)的標(biāo)識(shí)符就是一個(gè)類型形式參數(shù)。類似于方法的參數(shù),當(dāng)你使用的時(shí)候就替換一個(gè)實(shí)際參數(shù)進(jìn)去,只不過這個(gè)參數(shù)是個(gè)類型。在上面的例子中,我們就替換了一個(gè)

          Integer 類型進(jìn)去。

          在這里稍微說一下命名的規(guī)范,定義泛型中的形式參數(shù)時(shí),使用簡(jiǎn)潔有力又具有啟發(fā)性的名字,如果可以的話用單個(gè)字母更好。避免使用小寫,以免和普通的方法參數(shù)混淆。

          ?

          泛型與子類

          ?

          看看以下的例子語句合法嗎?

          ?

          List? < ?String? > ?ls? = ? new ?ArrayList? < ?String? > ?();? // 1?

          List?
          < ?Object? > ?lo? = ?ls;? // 2?

          ?

          2 句是行不通的,看看以下的語句:

          lo.add( new ?Object());? // ?3?

          String?s?
          = ?ls.get( 0 );? // ?4:?試圖將?Object?對(duì)象賦值給字符串對(duì)象,編譯錯(cuò)誤?

          簡(jiǎn)而言之就是,原有類型的繼承關(guān)系是不會(huì)反映到對(duì)應(yīng)的泛型上來,在上述情況下,任何兩個(gè)泛型類型都不存在繼承關(guān)系。那么,習(xí)慣了面向接口編程的我們?cè)鯓尤ミm應(yīng)這種嚴(yán)格的使用限制呢?

          ?

          通配符

          假如我們要用一個(gè)方法把一個(gè)容器內(nèi)的元素都 print 出來,可以用這樣的代碼來實(shí)現(xiàn) :

          void ?printCollection(Collection?c)? {?

          ?????????Iterator?i?
          = ?c.iterator();?

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

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

          ??????}

          ?????}
          ?

          如果我們用新的泛型和新的

          for 語句(你可以先不了解它,以后會(huì)談到)來嘗試同樣的功能,以下代碼可行嗎?

          ??void?printCollection(Collection?<?Object?>?c)?{?

          ??
          ????for?(Object?e?:?c)?{?

          ???????????System.out.println(e);?

          ?????????}

          }

          ?

          事實(shí)是,新的代碼的使用范圍非常有限,因?yàn)?/span> Collection < Object > 就只是一個(gè)放 Object 的容器泛類,它不是 任何其他 Collection 泛類的父類!真正擔(dān)任這個(gè)角色的是:

          Collection < ? >

          這個(gè)問號(hào)代表了未知, Collection < ? > 的元素可以是任何類型,這就是通配類型。

          現(xiàn)在我們可以這樣寫:

          void ?printCollection(Collection? < ? ? ? > ?c)? {?

          ??
          for ?(Object?e?:?c)? {?

          ?????????System.out.println(e);?

          ??????}
          ?
          }
          ?

          ?

          注意在循環(huán)里面,可以把元素賦值給一個(gè) Object 類型,因?yàn)闊o論 c 里放的是什么,它肯定是一個(gè) Object ,但向 c 里放置對(duì)象則是不安全的,因?yàn)椴恢?/span> c 的泛型是什么。如下面這樣是不行的:

          Collection? < ? ? ? > ?c? = ? new ?ArrayList? < ?String? > ?();?

          c.add(
          new ?Object());? // ?編譯錯(cuò)誤?

          ?

          add() 方面接納的是泛型的形式參數(shù)里描述的類型(或它的子類,希望你對(duì)此不感到混亂,呵呵。),然而此時(shí)我們只看到一個(gè)問號(hào),我們不知道它的類型參數(shù)是什么,當(dāng)然就不能放置對(duì)象進(jìn)去。唯一一個(gè)例外是 null ,它是任何一個(gè)類型的對(duì)象集的一分子。

          ?

          受限通配符

          省去一些說明性的代碼,以我們熟悉的幾何圖形家族例子來說明:

          ?

          public ? void ?drawAll(List? < ? ? ? extends ?Shape? > ?shapes)? {??} ?

          ?

          一個(gè) List<T> ,如果 T Shape 的子類,那么這個(gè) List 都可以被上面這個(gè)方法接納為參數(shù),這個(gè)就是受限的通配符。 Shape 就稱為這個(gè)通配符的上限。

          看看下面的代碼,怎樣?

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

          ??????shapes.add(
          0 ,? new ?Rectangle());? // ?compile-time?error!?

          }
          ?

          如上的方法依然是不行的,因?yàn)槲覀冎恢?/p> shapes Shape 或者其子類型的容器,然而具體類型是什么不知道,它未必是 Rectangle 的父類,所以不能放置元素進(jìn)去。

          ?

          總結(jié)起來,我們要了解的事情有:

          l???????? 泛型的用意

          l???????? 泛型的幾種形式(普通,通配符,受限通配符)

          l???????? 泛型與繼承關(guān)系一起使用時(shí)的易錯(cuò)傾向,尤其是向泛型容器添加元素的情況。

          posted on 2006-08-22 11:11 Binary 閱讀(547) 評(píng)論(1)  編輯  收藏 所屬分類: j2se

          評(píng)論

          # re: generic-泛型/類屬  回復(fù)  更多評(píng)論   

          學(xué)到不少。很想交朋友請(qǐng)教。
          2006-09-04 17:56 | liping
          主站蜘蛛池模板: 礼泉县| 同仁县| 商城县| 夹江县| 山西省| 通海县| 邻水| 准格尔旗| 慈利县| 罗甸县| 银川市| 吐鲁番市| 定西市| 昭通市| 东乡县| 建昌县| 泰顺县| 景东| 屯昌县| 萨嘎县| 万宁市| 达尔| 共和县| 林西县| 疏勒县| 临朐县| 宾川县| 买车| 枞阳县| 剑川县| 纳雍县| 抚顺县| 旺苍县| 清河县| 金门县| 绥宁县| 疏勒县| 余庆县| 壤塘县| 万州区| 凤庆县|