<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆 - 17  文章 - 49  trackbacks - 0
    <2006年8月>
    303112345
    6789101112
    13141516171819
    20212223242526
    272829303112
    3456789

    常用鏈接

    留言簿(1)

    隨筆分類(lèi)(17)

    隨筆檔案(17)

    相冊(cè)

    最新隨筆

    搜索

    •  

    最新評(píng)論

    閱讀排行榜

    評(píng)論排行榜

    2006 8 9 星期三

    管中窺虎

    在學(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ù)的,在這里補(bǔ)充說(shuō)明,雖然我希望以雙語(yǔ)寫(xiě)作,但是把英文文檔翻譯過(guò)來(lái)后再翻譯回去,似乎是件好傻的事情。。。所以這些翻譯并精簡(jiǎn)的文章算是個(gè)例外吧。

    第一道虎紋: generic -泛型 / 類(lèi)屬(二)

    泛型方法

    假設(shè)我們想把一個(gè)數(shù)組的元素都放到一個(gè)容器類(lèi)里,下面是第一次嘗試:

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

    ??
    for ?(Object?o?:?a)? {?
    ?????????c.add(o);?
    // ?compile?time?error?

    ??}

    }
    ?

    現(xiàn)在你應(yīng)該學(xué)會(huì)了不去犯初學(xué)者的錯(cuò)誤,用

    Collection < Object > 來(lái)作為參數(shù),你也許也發(fā)現(xiàn)了用 Collection < ? > 也不成,不知道就是不知道,不能放東西進(jìn)去。

    好了,主角登場(chǎng):泛型方法

    static ? < ?T? > ? void ?fromArrayToCollection(T[]?a,?Collection? < ?T? > ?c)? {?

    ??
    for ?(T?o?:?a)? {?
    ?????????c.add(o);?
    // ?correct?

    ????}

    }
    ?

    方法的聲明加入了類(lèi)型參數(shù),在上面這個(gè)方法里,如果

    c 的元素類(lèi)型是 a 的元素類(lèi)型的父類(lèi),就能成功執(zhí)行方法。下面這些代碼可以幫助你了解一下:

    Object[]?oa? = ? new ?Object[ 100 ];?

    Collection?
    < ?Object? > ?co? = ? new ?ArrayList? < ?Object? > ?();?

    fromArrayToCollection(oa,?co);?
    // ?T?inferred?to?be?Object?

    String[]?sa?
    = ? new ?String[ 100 ];?

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

    fromArrayToCollection(sa,?cs);?
    // ?T?inferred?to?be?String?

    fromArrayToCollection(sa,?co);?
    // ?T?inferred?to?be?Object?

    Integer[]?ia?
    = ? new ?Integer[ 100 ];?

    Float[]?fa?
    = ? new ?Float[ 100 ];?

    Number[]?na?
    = ? new ?Number[ 100 ];?

    Collection?
    < ?Number? > ?cn? = ? new ?ArrayList? < ?Number? > ?();?

    fromArrayToCollection(ia,?cn);?
    // ?T?inferred?to?be?Number?

    fromArrayToCollection(fa,?cn);?
    // ?T?inferred?to?be?Number?

    fromArrayToCollection(na,?cn);?
    // ?T?inferred?to?be?Number?

    fromArrayToCollection(na,?co);?
    // ?T?inferred?to?be?Object?

    fromArrayToCollection(na,?cs);?
    // ?compile-time?error?

    ??

    注意到我們并沒(méi)有真正的傳入一個(gè)類(lèi)型實(shí)參,而是由編譯器以方法的實(shí)際參數(shù)對(duì)象來(lái)推斷,它推斷出使得這次方法調(diào)用成立的類(lèi)型,并盡可能地特化這個(gè)類(lèi)型。比如說(shuō)如果

    T 推斷為 Number 依然成立的時(shí)候,就不會(huì)推斷為 Object

    現(xiàn)在看來(lái),泛型方法和通配符有些共通的地方,使得類(lèi)屬有一定的靈活性。那么什么時(shí)候用泛型方法,什么時(shí)候用通配符?看看下面的例子:

    ?interface?Collection?<?E?>?{?

    ???
    public?boolean?containsAll(Collection?<???>?c);?

    ???
    public?boolean?addAll(Collection?<???extends?E?>?c);?

    }
    ?

    以及:

    ?interface?Collection?<?E?>?{?

    ???
    public?<?T?>?boolean?containsAll(Collection?<?T?>?c);?

    ???
    public?<?T?extends?E?>?boolean?addAll(Collection?<?T?>?c);?

    ???
    //?注意類(lèi)型變量也可以有上限哦~?

    }
    ?

    這兩種方式都達(dá)成了同樣的目的,使得方法有了多態(tài)性。然而注意到在每個(gè)方法的聲明中,

    T 只出現(xiàn)了一次,這種情況下就應(yīng)該用通配符,通配符的主要目的就是提供彈性的泛化,而多態(tài)方法則用于表達(dá)兩個(gè)或多個(gè)參數(shù)間的依賴(lài)關(guān)系,你回過(guò)頭去看多態(tài)方法的第一個(gè)例子,是不是這個(gè)情況?如果不存在依賴(lài)關(guān)系需要表達(dá),就不應(yīng)該用多態(tài)方法,因?yàn)閺目勺x性上來(lái)說(shuō),通配符更清晰。

    ?

    而且有趣的是,它們并非水火不容,反而可以精妙配合,如下:

    ?class?Collections?{?

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

    }
    ?

    這個(gè)合作使得

    dest src 的依賴(lài)關(guān)系得以表達(dá),同時(shí)讓 src 的接納范疇擴(kuò)大了。假如我們只用泛型方法來(lái)實(shí)現(xiàn):

    ?class?Collections?{?

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

    }
    ?

    那么

    S 的存在就顯得有些不必要,有些不優(yōu)雅。總的來(lái)說(shuō),通配符更簡(jiǎn)潔清晰,只要情況允許就應(yīng)該首選。

    ?

    下面是給幾何圖形家族添加了一個(gè)有記憶功能的繪圖方法,展示了泛型方法的使用,有興趣就看看,略過(guò)也不影響下一步的學(xué)習(xí)。

    ?

    static ?List? < ?List? < ? ? ? extends ?Shape? >> ?history? = ? new ?ArrayList? < ?List? < ? ? ? extends ?Shape? >> ?();?

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

    ???????????history.addLast(shapes);?

    ???
    ????for?(Shape?s:?shapes)?{?

    ???????????s.draw(?
    this?);?

    ??????????}
    ?

    }
    ?

    ??

    ?

    這里又再談?wù)劽?guī)范的事情,用 T 來(lái)表示類(lèi)型( type )就是個(gè)很好的選擇,假如已經(jīng)沒(méi)有更多的背景信息的話,而在泛型方法里就是這樣子,我們只是想表達(dá)一個(gè)類(lèi)型。那么如果有多個(gè)參數(shù)出現(xiàn),那么用 T 的街坊鄰里就不錯(cuò), S 啊,什么的。如果方法出現(xiàn)在一個(gè)泛型類(lèi)里,就主要避免類(lèi)的泛型變量和方法的泛型變量同名混淆,同樣的,泛型類(lèi)的嵌套泛型類(lèi)也應(yīng)該注意。

    ?

    和遺老們打交道

    很顯然的,這個(gè)星球上存在的 java 代碼里,沒(méi)有引入泛型的還是多數(shù),它們也不會(huì)一夜走進(jìn)新社會(huì),怎么把它們轉(zhuǎn)換為泛型的會(huì)在晚些再談及,現(xiàn)在我們談?wù)労退鼈兇蚪坏赖氖虑椤_@包括兩方面:在引入泛型的代碼里使用老代碼,在老代碼上使用引入了泛型的代碼。

    ?

    首先看看前者,看例子:

    public ? interface ?Part? {??} ?

    public ? class ?Inventory? {? /** ?

    *?Adds?a?new?Assembly?to?the?inventory?database.?

    *?The?assembly?is?given?the?name?name,?and?consists?of?a?set?

    *?parts?specified?by?parts.?All?elements?of?the?collection?parts?

    *?must?support?the?Part?interface.?

    *
    */
    ?

    public ? static ? void ?addAssembly(String?name,?Collection?parts)? {??} ?

    public ? static ?Assembly?getAssembly(String?name)? {??} ?

    }
    ?

    public ? interface ?Assembly? {?

    ???Collection?getParts();?
    // ?Returns?a?collection?of?Parts?

    }
    ?

    public ? class ?Blade? implements ?Part?? {} ?

    public ? class ?Guillotine? implements ?Part? {} ?

    public ? class ?Main? {?

    ?public?static?void?main(String[]?args)?{?

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

    ???c.add(
    new?Guillotine())?;?

    ???c.add(
    new?Blade());?

    ???Inventory.addAssembly(”thingee”,?c);?

    ???Collection?
    <?Part?>?k?=?Inventory.getAssembly(”thingee”).getParts();//?琢磨一下這里??

    }
    ?

    }
    ?

    上面的代碼有沒(méi)有問(wèn)題?如果前面的內(nèi)容里你沒(méi)打瞌睡,你應(yīng)該發(fā)現(xiàn)注解處的那個(gè)語(yǔ)句很有問(wèn)題,類(lèi)型不安全問(wèn)題。我們稱(chēng)Collection這種不帶類(lèi)型參數(shù)的使用叫做原始類(lèi)型,編譯器無(wú)法保證這樣子的容器類(lèi)放了什么東西,但是編譯器會(huì)以不那么嚴(yán)格的標(biāo)準(zhǔn)去要求這個(gè)舊社會(huì)的人,否則,老代碼將完全不能在1.5里使用,編譯器會(huì)發(fā)出一個(gè)unchecked warning,怎么處理由你來(lái)決定。這樣的設(shè)計(jì)是符合實(shí)際的,否則就是和已有代碼徹底決裂。

    雖然這樣的調(diào)用會(huì)有錯(cuò)誤的風(fēng)險(xiǎn),但總比你完全不用泛型機(jī)制好,因?yàn)橹辽倌惚WC了在你的這一端的類(lèi)型安全,而且總有一天,英特那雄耐爾一定會(huì)實(shí)現(xiàn)。。。。 J

    嚴(yán)肅地回到我們的話題,既然有風(fēng)險(xiǎn),那么當(dāng)你得到了這樣的警告時(shí),小心檢查則是目前最好的對(duì)策。

    但是,如果你不理會(huì)一個(gè)這樣的警告,而且事實(shí)上你真的犯了一個(gè)類(lèi)型安全的錯(cuò)誤,會(huì)發(fā)生什么呢?

    消除與翻譯

    ?

    ?public?String?loophole(Integer?x)?{?List?<?String?>?ys?=?new?LinkedList?<?String?>?();?

    ???List?xs?
    =?ys;?

    ??????xs.add(x);?
    //?compile-time?unchecked?warning?

    ??????
    return?ys.iterator().next();?

    }
    ?

    這個(gè)警告所在的地方確實(shí)是有問(wèn)題的,一個(gè)

    Integer 被放入了一個(gè) List 中,這個(gè) List 只是原始類(lèi)型,所以編譯器只能給個(gè)警告,但事實(shí)上它又是指向了 ys 指向的對(duì)象,一個(gè)只放 String List ,在最后一句里,會(huì)發(fā)生什么事情?實(shí)際運(yùn)行起來(lái),這段代碼就等同于:

    ?public?String?loophole(Integer?x)?{?

    ???List?ys?
    =?new?LinkedList;?

    ???List?xs?
    =?ys;?

    ???xs.add(x);?

    ???
    return?(String)?ys.iterator().next();?//?run?time?error?

    }
    ?

    它們會(huì)得到同樣的錯(cuò)誤:一個(gè)

    ClassCastException

    為什么呢?因?yàn)榉盒驮?/font> java 編譯器里是以一種稱(chēng)為“消除”的前端轉(zhuǎn)換實(shí)現(xiàn)的,你幾乎,我說(shuō)幾乎,可以認(rèn)為是一種代碼到代碼的翻譯,象上面這樣,把帶泛型的版本翻譯成不帶泛型的版本,接下來(lái),當(dāng)代碼的執(zhí)行交到 JVM 的手里時(shí),它可不管你是哪朝代的人,有錯(cuò)就是有錯(cuò),類(lèi)型安全的基本政策不動(dòng)搖,即使你手里拽著 unchecked warnings 的證明。

    基本上,消除機(jī)制就是把類(lèi)型信息都扔掉了, List<String> 變成了 List ,上限通常都變成了 Object ,而且,當(dāng)轉(zhuǎn)換后的代碼不符合泛型里的類(lèi)型限制時(shí),就添加一個(gè)類(lèi)型轉(zhuǎn)換,就是上例中那個(gè) String 的轉(zhuǎn)換會(huì)出現(xiàn)的原因。

    消除機(jī)制的細(xì)節(jié)不是這里討論的內(nèi)容,這里簡(jiǎn)單的讓你了解一些需要了解的情況而已。

    ?

    ?

    假如你的代碼更新為:

    ?public?String?loophole(Integer?x)?{?

    ???List?ys?
    =?new?LinkedList;?

    ???List?xs?
    =?ys;?

    ???xs.add(x);?

    ????return?(String)?ys.iterator().next();?//?run?time?error?

    }
    ?

    而調(diào)用這個(gè)代碼的老客戶代碼是:

    public ? class ?Blade? implements ?Part?? {?

    }
    ?

    public ? class ?Guillotine? implements ?Part? {?

    }
    ?

    public ? class ?Main? {? public ? static ? void ?main(String[]?args)? {?

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

    ???c.add(
    new ?Guillotine())?;?

    ???c.add(
    new ?Blade());?

    ???Inventory.addAssembly(”thingee”,?c);?
    // ?1:?unchecked?warning?

    ???Collection?k?
    = ?Inventory.getAssembly(”thingee”).getParts();?

    }


    ?

    ?

    也就是在老代碼里調(diào)用泛型化了的代碼。

    如注解 1 所示,會(huì)有警告出現(xiàn),原因你也已經(jīng)了解了。你可以在編譯時(shí)選擇用 1.4 的環(huán)境,那么就不會(huì)有警告出現(xiàn),但同樣你也失去了 1.5 帶來(lái)的各種新特色了。

    ?

    這一篇到此為止,下一篇將看看一些還需要補(bǔ)充說(shuō)明的地方,但并不意味著內(nèi)容會(huì)很簡(jiǎn)單。

    ?

    posted on 2006-08-09 17:45 Ye Yiliang 閱讀(998) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): Java
    主站蜘蛛池模板: 亚洲黄色在线网站| 亚洲色丰满少妇高潮18p| 亚洲欧洲专线一区| 国产伦精品一区二区免费| 免费看片在线观看| 亚洲狠狠爱综合影院婷婷| 一区二区三区亚洲| 香港一级毛片免费看| 中文字幕视频免费| 亚洲高清免费视频| 亚洲an日韩专区在线| 久久嫩草影院免费看夜色| 大学生a级毛片免费观看| 亚洲AV无码一区东京热| 亚洲AV综合色区无码一二三区 | 女人被男人躁的女爽免费视频| 亚洲午夜久久久久久噜噜噜| 久久精品国产亚洲av麻豆蜜芽| 一级毛片不卡免费看老司机| 国国内清清草原免费视频99| 亚洲国产日韩在线视频| 久久久久亚洲AV无码去区首| 精品无码人妻一区二区免费蜜桃| 亚洲精品一级无码鲁丝片| 涩涩色中文综合亚洲| 男女作爱在线播放免费网站| 又粗又黄又猛又爽大片免费| 亚洲一区二区三区久久| 国产成人精品免费久久久久| 亚洲AⅤ视频一区二区三区| 色老板亚洲视频免在线观| 久99久精品免费视频热77| 亚洲国产成人五月综合网| 亚洲中文字幕一区精品自拍| 日本免费大黄在线观看| 亚洲色精品vr一区二区三区 | a在线观看免费视频| 四虎永久免费地址在线网站 | 爽爽爽爽爽爽爽成人免费观看| 国产精品免费看久久久久| 亚洲天堂2017无码中文|