Vincent

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

          generic-泛型/類屬(三)

          管中窺虎

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

          ?

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

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

          ?

          這一篇是接著上兩篇繼續(xù)的。

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

          一些零碎

          ?

          關(guān)于類:

          ?

          ?

          List? < ?String? > ?l1? = ? new ?ArrayList? < ?String? > ?();?

          List?
          < ?Integer? > ?l2? = ? new ?ArrayList? < ?Integer? > ?();?

          System.out.println(l1.getClass()?
          == ?l2.getClass());?

          ?

          這樣的代碼打印出什么?腦子有沒(méi)有點(diǎn)混亂?事實(shí)是 true ,雖然類型參數(shù)不一樣,但它們?cè)谶\(yùn)行時(shí)終歸是同一個(gè)類。一個(gè) class 可以有不同的 type 。由于靜態(tài)的變量和方法是被這個(gè)類的所有實(shí)例共享的,所以在靜態(tài)的方法,初始化塊,靜態(tài)變量的聲明或初始化中引用類型變量(前面的 T 這一類的東西)是非法的。

          ?

          關(guān)于轉(zhuǎn)換和 instanceOf

          ?

          正因?yàn)轭惖念愋褪潜凰械膶?shí)例共享的,所以去問(wèn)一個(gè)實(shí)例是否為特定的類型是無(wú)意義的。

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

          if ?(cs? instanceof ?Collection? < ?String? > ?)? {??} ? // ?illegal?

          這樣的代碼是非法的。

          ?

          Collection? < ?String? > ?cstr? = ?(Collection? < ?String? > ?)?cs;? // ?unchecked?warning?

          ?

          同樣的,上面這行代碼將會(huì)有 unchecked warning 因?yàn)樗噲D做的類型檢查,是根本不會(huì)在運(yùn)行時(shí)被執(zhí)行的。同樣的,類型變量也是無(wú)效的:

          ?

          < ?T? > ?T?badCast(T?t,?Object?o)? {? return ?(T)?o;? // ?unchecked?warning?

          }
          ?

          ?

          總而言之,類型變量在運(yùn)行時(shí)是不存在的,意味著它們不會(huì)對(duì)運(yùn)行表現(xiàn)增加任何時(shí)間上或者空間上的累贅,這樣挺好,但同時(shí)也意味著,別指望用它們來(lái)做類型轉(zhuǎn)換。

          ?

          ?

          關(guān)于數(shù)組

          ?

          一個(gè)數(shù)組對(duì)象的元素類型是不能為類型變量或者帶類型參數(shù)的類型,除非是一個(gè)非受限通配符類型,你可以聲明一個(gè)元素類型為類型變量或者帶類型參數(shù)的類型的數(shù)組類型,但是不能聲明這樣的數(shù)組對(duì)象。好吧,你舌頭打結(jié)了吧?說(shuō)實(shí)話,在翻譯這段話前,我不僅舌頭打結(jié),連神經(jīng)都快打結(jié)了,即使如此,我還是不能肯定我翻譯的是否正確。原文如下:

          The component type of an array object may not be a type variable or a parameterized

          type, unless it is an (unbounded) wildcard type.You can declare array types whose

          element type is a type variable or a parameterized type, but not array objects.

          通過(guò)閱讀下面的代碼,也許幫助你理解這團(tuán)亂麻,之所以有上面這樣的復(fù)雜規(guī)定,是為了避免這樣的情形:

          ?

          List? < ?String? > ?[]?lsa? = ? new ?List? < ?String? > ?[ 10 ];? // ?實(shí)際上不被允許,現(xiàn)在只是假設(shè)可以這樣寫(xiě)?

          Object?o?
          = ?lsa;?

          Object[]?oa?
          = ?(Object[])?o;?

          List?
          < ?Integer? > ?li? = ? new ?ArrayList? < ?Integer? > ?();?

          li.add(
          new ?Integer( 3 ));?

          oa[
          1 ]? = ?li;? // ?有錯(cuò),但通過(guò)了運(yùn)行時(shí)的檢查?

          String?s?
          = ?lsa[ 1 ].get( 0 );? // ?run-time?error?-?ClassCastException?

          ?

          如果運(yùn)行了數(shù)組元素為帶類型變量的類型,就會(huì)出現(xiàn)這樣的情況,明明通過(guò)了編譯,沒(méi)有任何 unchecked warning ,但在運(yùn)行時(shí)卻出錯(cuò)。所以泛型必須設(shè)計(jì)為這個(gè)樣子,以保證:如果你的整個(gè)應(yīng)用程序在 jdk1.5 下通過(guò)了編譯而且沒(méi)有 unchecked warning ,那它就是類型安全的。

          然而,你還可以用通配符數(shù)組,接下來(lái)是以上代碼的兩個(gè)改版。

          ?

          第一個(gè)讓數(shù)組類和數(shù)組對(duì)象都用了通配符,在最后一句,如果要賦值給 String ,就必須用顯示的類型轉(zhuǎn)換,雖然出了錯(cuò),那么可以說(shuō)這不是機(jī)制的錯(cuò)誤了,而是程序員的錯(cuò)。

          ?

          List? < ? ? ? > ?[]?lsa? = ? new ?List? < ? ? ? > ?[ 10 ];? // ?ok,?array?of?unbounded?wildcard?type?

          Object?o?
          = ?lsa;?

          Object[]?oa?
          = ?(Object[])?o;?

          List?
          < ?Integer? > ?li? = ? new ?ArrayList? < ?Integer? > ?();?

          li.add(
          new ?Integer( 3 ));?

          oa[
          1 ]? = ?li;? // ?correct?

          String?s?
          = ?(String)?lsa[ 1 ].get( 0 );? // ?run?time?error,?but?cast?is?explicit?

          ?

          ?

          ?

          在第二個(gè)版本里,我們定義了帶類型參數(shù)的類型的數(shù)組類,但數(shù)組對(duì)象用了通配符,這樣是合法的,但不安全,產(chǎn)生了 unchecked warning ,而且最終也確實(shí)出錯(cuò)了。但至少,我們得到了警告。

          ?

          List? < ?String? > ?[]?lsa? = ? new ?List? < ? ? ? > ?[ 10 ];? // ?unchecked?warning?-?this?is?unsafe!?

          Object?o?
          = ?lsa;?

          Object[]?oa?
          = ?(Object[])?o;?

          List?
          < ?Integer? > ?li? = ? new ?ArrayList? < ?Integer? > ?();?

          li.add(
          new ?Integer( 3 ));?

          oa[
          1 ]? = ?li;? // ?correct?

          String?s?
          = ?lsa[ 1 ].get( 0 );? // ?run?time?error,?but?we?were?warned?

          ?

          ?

          ?

          同樣的,試圖創(chuàng)造一個(gè)元素類型是類型變量的數(shù)組對(duì)象是通過(guò)不了編譯的:

          ?

          這些錯(cuò)誤的原因歸結(jié)起來(lái)也是我們之前討論過(guò)的了,就是因?yàn)檫\(yùn)行時(shí),這些類型變量是不存在的,無(wú)法決定數(shù)組的實(shí)際類型。

          要突破這些限制,可以使用 類名稱字面常量( class literal )作為運(yùn)行時(shí)類型標(biāo)記,請(qǐng)看下文。

          ?

          ?

          以類名稱字面常量作為運(yùn)行時(shí)類型標(biāo)記

          Jdk1.5 里, java.lang.Class 是泛型的,這就有趣了, Class 類有個(gè)類型參數(shù) T ,那 T 代表什么? T 代表了 Class 類的對(duì)象所代表的類型,又來(lái)繞口令了不是?

          舉例吧: String.class 的類型就是 Class<String> Serializable.class 的類型就是 Class < Serializable > 。現(xiàn)在 Class 類的 newInstance() 方法返回一個(gè) T ,你現(xiàn)在在創(chuàng)造對(duì)象的時(shí)候獲得的類型更加精確了。( 1.4 里是固定地返回 Object

          假設(shè)你要寫(xiě)一個(gè)工具方法,從數(shù)據(jù)庫(kù)里執(zhí)行一個(gè) SQL 查詢,將符合查詢的結(jié)果以對(duì)象集合返回,有一鐘方法就是顯示的使用一個(gè)工廠對(duì)象,如下:

          interface ?Factory? < ?T? > ? {?T?make();?} ?

          public ? < ?T? > ?Collection? < ?T? > ?select(Factory? < ?T? > ?factory,?String?statement)? {?

          ???????Collection?
          < ?T? > ?result? = ? new ?ArrayList? < ?T? > ?();?

          /* ?run?sql?query?using?jdbc? */ ?

          ?????
          for ?( /* ?iterate?over?jdbc?results? */ ?)? {?

          ??????T?item?
          = ?factory.make();?

          /* ?use?reflection?and?set?all?of?item’s?fields?from?sql?results? */ ?

          ??????result.add(item);?

          ??????}
          ?

          ??????
          return ?result;?

          }
          ?

          你可以這樣調(diào)用它:

          ?

          select( new ?Factory? < ?EmpInfo? > ?()?{?

          public ?EmpInfo?make()?{?

          ???return ? new ?EmpInfo();?

          }?

          }?,?”selection?string”);?

          ?

          或者聲明一個(gè)實(shí)現(xiàn) Factory 接口的類 EmpInfoFactory

          class ?EmpInfoFactory? implements ?Factory? < ?EmpInfo? > ??{?

          public ?EmpInfo?make()?{?

          ??
          return ? new ?EmpInfo();?

          }?

          }?

          然后這樣調(diào)用它:

          select(getMyEmpInfoFactory(),?”selection?string”);?

          ?

          這兩個(gè)辦法的缺點(diǎn)就是,你要么在調(diào)用處寫(xiě)上羅嗦的工廠類,要么為每個(gè)類都寫(xiě)一個(gè)工廠類,在每個(gè)調(diào)用的地方傳入一個(gè)工廠對(duì)象,這看起來(lái)也不怎么自然。

          類名稱字面常量來(lái)作為一個(gè)工廠對(duì)象就很自然,它可以用于反射機(jī)制,如果不用泛型,可以這樣寫(xiě):

          C...ollection emps = sqlUtility.select(EmpInfo.class, ”select * from emps”);

          public ? static ?Collection?select(Class?c,?String?sqlStatement)?{?

          ???Collection?result?
          = ? new ?ArrayList();?

          ???
          /* ?run?sql?query?using?jdbc? */ ?

          ???
          for ?(? /* ?iterate?over?jdbc?results? */ ?)?{?

          ???????Object?item?
          = ?c.newInstance();?

          ??????
          /* ?use?reflection?and?set?all?of?item’s?fields?from?sql?results? */ ?

          ??????result.add(item);?

          ???}?
          return ?result;?

          }?

          ?

          然而這樣我們得不到我們想要的包含確切的類型的對(duì)象集,而現(xiàn)在 Class 是泛型的了,我們可以用它來(lái)達(dá)到目的:

          Collection? < ?EmpInfo? > ?emps? = ??sqlUtility.select(EmpInfo. class ,?”select? * ?from?emps”);?

          public ? static ? < ?T? > ?Collection? < ?T? > ?select(Class? < ?T? > ?c,?String?sqlStatement)?{?Collection? < ?T? > ?result? = ? new ?ArrayList? < ?T? > ();?

          ???
          /* ?run?sql?query?using?jdbc? */ ?

          ???
          for ?(? /* ?iterate?over?jdbc?results? */ ?)?{?T?item? = ?c.newInstance();?

          ??????
          /* ?use?reflection?and?set?all?of?item’s?fields?from?sql?results? */ ?

          ??????result.add(item);?

          ???}?
          return ?result;?

          }?

          精確的類型,類型安全。齊活兒了。

          ?

          這一篇到此為止,泛型的部分還剩下比較復(fù)雜的兩個(gè)部分,經(jīng)過(guò)考慮后決定延后完成,先完成 tiger 的其他幾個(gè)簡(jiǎn)易的新特色會(huì)比較有趣些。

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

          主站蜘蛛池模板: 德兴市| 瑞丽市| 定南县| 潜江市| 正定县| 霍林郭勒市| 舟山市| 铜鼓县| 团风县| 宁海县| 锡林郭勒盟| 太白县| 班戈县| 双牌县| 台前县| 大洼县| 日喀则市| 阳春市| 北安市| 呼伦贝尔市| 神农架林区| 鄂尔多斯市| 辽阳县| 嘉峪关市| 乐清市| 收藏| 德惠市| 固镇县| 邹平县| 嘉义市| 福海县| 江华| 红桥区| 万荣县| 德化县| 嫩江县| 东丽区| 甘洛县| 年辖:市辖区| 库尔勒市| 崇州市|