乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      掌握 Java 泛型類(lèi)型(一)

       pablo3518 2007-06-13

      數(shù)據(jù)類(lèi)型轉(zhuǎn)換和錯(cuò)誤
      為理解泛型類(lèi)型為何如此有用,我們要將注意力轉(zhuǎn)向 Java 語(yǔ)言中最容易引發(fā)錯(cuò)誤的因素之一 - 需要不斷地將表達(dá)式向下類(lèi)型轉(zhuǎn)換(downcast)為比其靜態(tài)類(lèi)型更為具體的數(shù)據(jù)類(lèi)型(請(qǐng)參閱參考資料中的“The Double Descent bug pattern”,以了解進(jìn)行數(shù)據(jù)類(lèi)型轉(zhuǎn)換時(shí),可能會(huì)碰到的麻煩的某些方面)。

      程序中的每個(gè)向下類(lèi)型轉(zhuǎn)換對(duì)于 ClassCastException 而言都是潛在的危險(xiǎn),應(yīng)當(dāng)盡量避免它們。但是在 Java 語(yǔ)言中它們通常是無(wú)法避免的,即便在設(shè)計(jì)優(yōu)良的程序中也是如此。

      在 Java 語(yǔ)言中進(jìn)行向下類(lèi)型轉(zhuǎn)換最常見(jiàn)的原因在于,經(jīng)常以專(zhuān)用的方式來(lái)使用類(lèi),這限制了方法調(diào)用所返回的參數(shù)可能的運(yùn)行時(shí)類(lèi)型。例如,假定往 Hashtable 中添加元素并從中檢索元素。那么在給定的程序中,被用作鍵的元素類(lèi)型和存儲(chǔ)在散列表中的值類(lèi)型,將不能是任意對(duì)象。通常,所有的鍵都是某一特定類(lèi)型的實(shí)例。同樣地,存儲(chǔ)的值將共同具有比 Object 更具體的公共類(lèi)型。

      但是在目前現(xiàn)有的 Java 語(yǔ)言版本中,不可能將散列表的特定鍵和元素聲明為比 Object 更具體的類(lèi)型。在散列表上執(zhí)行插入和檢索操作的類(lèi)型特征符告訴我們只能插入和刪除任意對(duì)象。例如,putget 操作的說(shuō)明如下所示:

      清單 1. 插入/檢索類(lèi)型說(shuō)明表明只能是任意對(duì)象

      class Hashtable {  Object put(Object key, Object value) {...}  Object get(Object key) {...}  ...}


      因此,當(dāng)我們從類(lèi) Hashtable 的實(shí)例檢索元素時(shí),比如,即使我們知道在 Hashtable 中只放了 String,而類(lèi)型系統(tǒng)也只知道所檢索的值是 Object 類(lèi)型。在對(duì)檢索到的值進(jìn)行任何特定于 String 的操作之前,必須將它強(qiáng)制轉(zhuǎn)換為 String,即使是將檢索到的元素添加到同一代碼塊中,也是如此!

      清單 2. 將檢索到的值強(qiáng)制轉(zhuǎn)換成 String

      import java.util.Hashtable;class Test {  public static void main(String[] args) {    Hashtable h = new Hashtable();    h.put(new Integer(0), "value");    String s = (String)h.get(new Integer(0));    System.out.println(s);  }}


      請(qǐng)注意 main 方法主體部分的第三行中需要進(jìn)行的數(shù)據(jù)類(lèi)型轉(zhuǎn)換。因?yàn)?Java 類(lèi)型系統(tǒng)相當(dāng)薄弱,因此代碼會(huì)因象上面那樣的數(shù)據(jù)類(lèi)型轉(zhuǎn)換而漏洞百出。這些數(shù)據(jù)類(lèi)型轉(zhuǎn)換不僅使 Java 代碼變得更加拖沓冗長(zhǎng),而且它們還降低了靜態(tài)類(lèi)型檢查的價(jià)值(因?yàn)槊總€(gè)數(shù)據(jù)類(lèi)型轉(zhuǎn)換都是一個(gè)選擇忽略靜態(tài)類(lèi)型檢查的偽指令)。我們?cè)撊绾螖U(kuò)展該類(lèi)型系統(tǒng),從而不必回避它呢?

      用泛型類(lèi)型來(lái)解決問(wèn)題!
      要消除如上所述的數(shù)據(jù)類(lèi)型轉(zhuǎn)換,有一種普遍的方法,就是用泛型類(lèi)型來(lái)增大 Java 類(lèi)型系統(tǒng)??梢詫⒎盒皖?lèi)型看作是類(lèi)型“函數(shù)”;它們通過(guò)類(lèi)型變量進(jìn)行參數(shù)化,這些類(lèi)型變量可以根據(jù)上下文用各種類(lèi)型參數(shù)進(jìn)行實(shí)例化。

      例如,與簡(jiǎn)單地定義類(lèi) Hashtable 不同,我們可以定義泛型類(lèi) Hashtable<Key, Value>,其中 KeyValue 是類(lèi)型參數(shù)。除了類(lèi)名后跟著尖括號(hào)括起來(lái)的一系列類(lèi)型參數(shù)聲明之外,在 Tiger 中定義這樣的泛型類(lèi)的語(yǔ)法和用于定義普通類(lèi)的語(yǔ)法很相似。例如,可以按照如下所示的那樣定義自己的泛型 Hashtable 類(lèi):

      清單 3. 定義泛型 Hashtable 類(lèi)

      class Hashtable<Key, Value> { ... }


      然后可以引用這些類(lèi)型參數(shù),就像我們?cè)陬?lèi)定義主體內(nèi)引用普通類(lèi)型那樣,如下所示:

      清單 4. 像引用普通類(lèi)型那樣引用類(lèi)型參數(shù)
      class Hashtable<Key, Value> {  ...  Value put(Key k, Value v) {...}  Value get(Key k) {...}}

      類(lèi)型參數(shù)的作用域就是相應(yīng)類(lèi)定義的主體部分(除了靜態(tài)成員之外)(在下一篇文章中,我們將討論為何 Tiger 實(shí)現(xiàn)中有這樣的“怪習(xí)”,即必須對(duì)靜態(tài)成員進(jìn)行此項(xiàng)限制。請(qǐng)留意?。?。

      創(chuàng)建一個(gè)新的 Hashtable 實(shí)例時(shí),必須傳遞類(lèi)型參數(shù)以指定 KeyValue 的類(lèi)型。傳遞類(lèi)型參數(shù)的方式取決于我們打算如何使用 Hashtable。在上面的示例中,我們真正想要做的是創(chuàng)建 Hashtable 實(shí)例,它只將 Integer 映射為 String??梢杂眯碌?Hashtable 類(lèi)來(lái)完成這件事:

      清單 5. 創(chuàng)建將 Integer 映射為 String 的實(shí)例

      import java.util.Hashtable;class Test {  public static void main(String[] args) {    Hashtable<Integer, String> h = new Hashtable<Integer, String>();    h.put(new Integer(0), "value");    ...  }}


      現(xiàn)在不再需要數(shù)據(jù)類(lèi)型轉(zhuǎn)換了。請(qǐng)注意用來(lái)實(shí)例化泛型類(lèi) Hashtable 的語(yǔ)法。就像泛型類(lèi)的類(lèi)型參數(shù)用尖括號(hào)括起來(lái)那樣,泛型類(lèi)型應(yīng)用程序的參數(shù)也是用尖括號(hào)括起來(lái)的。

      清單 6. 除去不必要的數(shù)據(jù)類(lèi)型轉(zhuǎn)換

      ...String s = h.get("key");System.out.println(s);


      當(dāng)然,程序員若只是為了能使用泛型類(lèi)型而必須重新定義所有的標(biāo)準(zhǔn)實(shí)用程序類(lèi)(比如 HashtableList)的話(huà),則可能會(huì)是一項(xiàng)浩大的工程。幸好,Tiger 為用戶(hù)提供了所有 Java 集合類(lèi)的泛型版本,因此我們不必自己動(dòng)手來(lái)重新定義它們了。此外,這些類(lèi)能與舊代碼和新的泛型代碼一起無(wú)縫工作(下個(gè)月,我們會(huì)說(shuō)明如何做到這一點(diǎn))。

      Tiger 的基本類(lèi)型限制
      Tiger 中類(lèi)型變量的限制之一就是,它們必須用引用類(lèi)型進(jìn)行實(shí)例化 - 基本類(lèi)型不起作用。因此,在上面這個(gè)示例中,無(wú)法完成創(chuàng)建從 int 映射到 StringHashtable。

      這很遺憾,因?yàn)檫@意味著只要您想把基本類(lèi)型用作泛型類(lèi)型的參數(shù),您就必須把它們組裝為對(duì)象。另一方面,當(dāng)前的這種情況是最糟的;您不能將 int 作為鍵傳遞給 Hashtable,因?yàn)樗械逆I都必須是 Object 類(lèi)型。

      我們真正想看到的是,基本類(lèi)型可以自動(dòng)進(jìn)行包裝(boxing)和解包裝(unboxing),類(lèi)似于用 C# 所進(jìn)行的操作(或者比后者更好)。遺憾的是,Tiger 不打算包括基本類(lèi)型的自動(dòng)包裝(但是人們可以一直期待 Java 1.6 中出現(xiàn)該功能?。?/p>

      受限泛型
      有時(shí)我們想限制可能出現(xiàn)的泛型類(lèi)的類(lèi)型實(shí)例化。在上面這個(gè)示例中,類(lèi) Hashtable 的類(lèi)型參數(shù)可以用我們想用的任何類(lèi)型參數(shù)進(jìn)行實(shí)例化,但是對(duì)于其它某些類(lèi),我們或許想將可能的類(lèi)型參數(shù)集限定為給定類(lèi)型范圍內(nèi)的子類(lèi)型。

      例如,我們可能想定義泛型 ScrollPane 類(lèi),它引用普通的帶有滾動(dòng)條功能的 Pane。被包含的 Pane 的運(yùn)行時(shí)類(lèi)型通常會(huì)是類(lèi) Pane 的子類(lèi)型,但是靜態(tài)類(lèi)型就只是 Pane。

      有時(shí)我們想用 getter 檢索被包含的 Pane,但是希望 getter 的返回類(lèi)型盡可能具體些。我們可能想將類(lèi)型參數(shù) MyPane 添加到 ScrollPane 中,該類(lèi)型參數(shù)可以用 Pane 的任何子類(lèi)進(jìn)行實(shí)例化。然后可以用這種形式的子句:extends Bound 來(lái)說(shuō)明 MyPane 的聲明,從而來(lái)設(shè)定 MyPane 的范圍:

      清單 7. 用 extends 子句來(lái)說(shuō)明 MyPane 聲明

      class ScrollPane<MyPane extends Pane> { ... }


      當(dāng)然,我們可以完全不使用顯式的范圍,只要能確保沒(méi)有用不適當(dāng)?shù)念?lèi)型來(lái)實(shí)例化類(lèi)型參數(shù)。

      為什么要自找麻煩在類(lèi)型參數(shù)上設(shè)定范圍呢?這里有兩個(gè)原因。首先,范圍使我們?cè)黾恿遂o態(tài)類(lèi)型檢查功能。有了靜態(tài)類(lèi)型檢查,就能保證泛型類(lèi)型的每次實(shí)例化都符合所設(shè)定的范圍。

      其次,因?yàn)槲覀冎李?lèi)型參數(shù)的每次實(shí)例化都是這個(gè)范圍之內(nèi)的子類(lèi),所以可以放心地調(diào)用類(lèi)型參數(shù)實(shí)例出現(xiàn)在這個(gè)范圍之內(nèi)的任何方法。如果沒(méi)有對(duì)參數(shù)設(shè)定顯式的范圍,那么缺省情況下范圍是 Object,這意味著我們不能調(diào)用范圍實(shí)例在 Object 中未曾出現(xiàn)的任何方法。

      多態(tài)方法
      除了用類(lèi)型參數(shù)對(duì)類(lèi)進(jìn)行參數(shù)化之外,用類(lèi)型參數(shù)對(duì)方法進(jìn)行參數(shù)化往往也同樣很有用。泛型 Java 編程用語(yǔ)中,用類(lèi)型進(jìn)行參數(shù)化的方法被稱(chēng)為多態(tài)方法(Polymorphic method)。

      多態(tài)方法之所以有用,是因?yàn)橛袝r(shí)候,在一些我們想執(zhí)行的操作中,參數(shù)與返回值之間的類(lèi)型相關(guān)性原本就是泛型的,但是這個(gè)泛型性質(zhì)不依賴(lài)于任何類(lèi)級(jí)的類(lèi)型信息,而且對(duì)于各個(gè)方法調(diào)用都不相同。

      例如,假定想將 factory 方法添加到 List 類(lèi)中。這個(gè)靜態(tài)方法只帶一個(gè)參數(shù),也將是 List 唯一的元素(直到添加了其它元素)。因?yàn)槲覀兿M?List 成為其所包含的元素類(lèi)型的泛型,所以希望靜態(tài) factory 方法帶有類(lèi)型變量 T 這一參數(shù)并返回 List<T> 的實(shí)例。

      但是我們確實(shí)希望該類(lèi)型變量 T 能在方法級(jí)別上進(jìn)行聲明,因?yàn)樗鼤?huì)隨每次單獨(dú)的方法調(diào)用而發(fā)生改變(而且,正如我在下一篇文章中將討論的那樣,Tiger 設(shè)計(jì)的“怪習(xí)”規(guī)定靜態(tài)成員不在類(lèi)級(jí)類(lèi)型參數(shù)的范疇之內(nèi))。Tiger 讓我們通過(guò)將類(lèi)型參數(shù)作為方法聲明的前綴,從而在單獨(dú)的方法級(jí)別上聲明類(lèi)型參數(shù)。例如,可以按照如下所示的那樣為 factory 方法 make 添加前綴:

      清單 8. 將類(lèi)型參數(shù)作為前綴添加到方法聲明

      class Utilities {   <T extends Object> public static List<T> make(T first) {     return new List<T>(first);   }}


      除了多態(tài)方法中所增加的靈活性之外,Tiger 中還增加了一個(gè)優(yōu)點(diǎn)。Tiger 使用類(lèi)型推斷機(jī)制,根據(jù)參數(shù)類(lèi)型來(lái)自動(dòng)推斷出多態(tài)方法的類(lèi)型。這可以大大減少方法調(diào)用的繁瑣和復(fù)雜性。例如,如果想調(diào)用 make 方法來(lái)構(gòu)造包含 new Integer(0)List<Integer> 新實(shí)例,那么只需編寫(xiě):

      清單 9. 強(qiáng)制 make 構(gòu)造新實(shí)例

      Utilities.make(Integer(0))


      然后會(huì)自動(dòng)地從方法參數(shù)中推斷出類(lèi)型參數(shù)的實(shí)例化。

      結(jié)束語(yǔ)
      正如我們所見(jiàn)到的那樣,在 Java 語(yǔ)言中添加泛型類(lèi)型肯定會(huì)大大增強(qiáng)我們使用靜態(tài)類(lèi)型系統(tǒng)的能力。學(xué)習(xí)如何使用泛型類(lèi)型相當(dāng)簡(jiǎn)單,但是同樣也需要避免一些缺陷。在接下來(lái)的文章中,我們將討論如何充分使用將出現(xiàn)在 Tiger 中的泛型類(lèi)型的特定表現(xiàn),以及一些缺陷。我們還將研究對(duì)泛型 Java 類(lèi)型工具的擴(kuò)展,我們期盼這些工具可以出現(xiàn)在仍處于設(shè)計(jì)階段的 Java 平臺(tái)之中。

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀(guān)點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶(hù) 評(píng)論公約

        類(lèi)似文章 更多