空間站

          北極心空

            BlogJava :: 首頁(yè) :: 聯(lián)系 :: 聚合  :: 管理
            15 Posts :: 393 Stories :: 160 Comments :: 0 Trackbacks

          公告

          本博客主要是在本人收集網(wǎng)上一些精彩技術(shù)文章,有時(shí)可能因疏忽轉(zhuǎn)載的時(shí)候沒(méi)有說(shuō)明轉(zhuǎn)載出處和作者,如果您認(rèn)為哪篇文章侵犯了你的版權(quán),請(qǐng)通知本人: EMAIL:luwei-80@163.com 歡迎您光臨本博客!

          常用鏈接

          留言簿(15)

          我參與的團(tuán)隊(duì)

          搜索

          •  

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

                                                                             (一)
          JDK 5.0
          (也叫做 Java 5.0 或者 Tiger Java 語(yǔ)來(lái)了一些大的化。其中最重要的化就是引入了泛型 —— 支持定義帶有抽象類(lèi)型參數(shù)的類(lèi)些參數(shù)由您在實(shí)例化時(shí)指定。泛型提高大型程序的類(lèi)型安全和可維護(hù)來(lái)了很大的潛力。

          泛型與 JDK 5.0 中其他幾個(gè)新的語(yǔ)言特性相互協(xié)作,包括增強(qiáng) for 環(huán)(有時(shí)叫做 foreach 或者 for/in 環(huán))、枚enumeration)和自動(dòng)裝箱(autoboxing)。

          本教程解了在 Java 語(yǔ)言中引入泛型的動(dòng)機(jī),詳細(xì)了泛型的語(yǔ)法和語(yǔ)義,并述了如何在自己的類(lèi)中使用泛型。

          本教程針對(duì)中高級(jí) Java 開(kāi)發(fā),他想要了解針對(duì)泛型的新語(yǔ)言支持是如何工作的。本教程假定者熟悉在 Java 語(yǔ)言中開(kāi)發(fā)接口和類(lèi),并且具有基本的面向對(duì)設(shè)計(jì)技能。

          泛型語(yǔ)言特性只在 JDK 5.0 及以后版本中可用。如果您是基于早的 JDK 版本開(kāi)發(fā)軟件,那在遷移到 JDK 5.0 或以后版本之前,將無(wú)法在代中使用泛型特性。

          了使用泛型,必具有一個(gè) JDK 5.0 開(kāi)發(fā)環(huán)境。可以從 Sun 公司的 Web 站點(diǎn)免費(fèi) JDK 5.0 (http://java.sun.com/j2se/1.5.0/index.jsp)

          是泛型?

          泛型(Generic type 或者 generics)是對(duì) Java 語(yǔ)言的類(lèi)型系統(tǒng)的一種擴(kuò)展,以支持創(chuàng)建可以按類(lèi)進(jìn)行參數(shù)化的類(lèi)。可以把類(lèi)型參數(shù)看作是使用參數(shù)類(lèi)時(shí)指定的類(lèi)型的一個(gè)占位符,就像方法的形式參數(shù)是運(yùn)行時(shí)傳遞的占位符一

          可以在集合框架(Collection framework)中看到泛型的動(dòng)機(jī)。例如,Map 類(lèi)您向一個(gè) Map 添加任意類(lèi)對(duì)象,即使最常見(jiàn)的情況是在定映射(map)中保存某個(gè)特定類(lèi)型(比如 String)的對(duì)象。

          Map.get() 被定義為返回 Object,所以一般必 Map.get() 結(jié)強(qiáng)類(lèi)轉(zhuǎn)換為期望的類(lèi)型,如下面的代所示:

          Map m = new HashMap();

          m.put("key", "blarg");

          String s = (String) m.get("key");

          程序通過(guò)編譯,必 get() 結(jié)強(qiáng)類(lèi)轉(zhuǎn)換為 String,并且希望結(jié)果真的是一個(gè) String。但是有可能某人已經(jīng)映射中保存了不是 String 西,這樣,上面的代將會(huì)拋出 ClassCastException

          理想情況下,您可能會(huì)得出這樣一個(gè)點(diǎn),即 m 是一個(gè) Map,它將 String 映射到 String 可以您消除代中的強(qiáng)類(lèi)轉(zhuǎn)換,同時(shí)獲得一個(gè)附加的類(lèi)檢查層該檢查層可以防止有人將錯(cuò)誤類(lèi)型的保存在集合中。就是泛型所做的工作。

          泛型的好

          Java 語(yǔ)言中引入泛型是一個(gè)大的功能增強(qiáng)。不僅語(yǔ)言、類(lèi)型系統(tǒng)編譯器有了大的化,以支持泛型,而且類(lèi)庫(kù)進(jìn)行了大翻修,所以多重要的類(lèi),比如集合框架,都已經(jīng)泛型化的了。這帶來(lái)了很多好

          類(lèi)型安全。泛型的主要目標(biāo)是提高 Java 程序的類(lèi)型安全。通過(guò)知道使用泛型定量的類(lèi)型限制,編譯器可以在一個(gè)高得多的程度上驗(yàn)證類(lèi)型假設(shè)。沒(méi)有泛型,些假設(shè)就只存在于程序頭腦中(或者如果幸運(yùn)的存在于代中)。

          Java 程序中的一流行技術(shù)是定義這樣的集合,即它的元素或是公共類(lèi)型的,比如“String 列表”或者“String String 的映射”。過(guò)量聲明中捕獲這一附加的類(lèi)型信息,泛型允許編譯實(shí)些附加的類(lèi)束。類(lèi)錯(cuò)誤現(xiàn)在就可以在編譯時(shí)被捕了,而不是在運(yùn)行時(shí)當(dāng)作 ClassCastException 展示出來(lái)。將類(lèi)檢查從運(yùn)行時(shí)挪到編譯時(shí)有助于您更容易找到錯(cuò)誤,并可提高程序的可靠性。

          消除強(qiáng)類(lèi)轉(zhuǎn)換泛型的一個(gè)附是,消除源代中的強(qiáng)類(lèi)轉(zhuǎn)換使得代更加可,并且減少了出錯(cuò)機(jī)會(huì)。

          盡管減少強(qiáng)類(lèi)轉(zhuǎn)換可以降低使用泛型類(lèi)的代羅嗦程度,但是聲明泛型量會(huì)來(lái)相應(yīng)羅嗦。比下面兩個(gè)代例子。

          不使用泛型:

          List li = new ArrayList();

          li.put(new Integer(3));

          Integer i = (Integer) li.get(0);

          使用泛型:

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

          li.put(new Integer(3));

          Integer i = li.get(0);

          簡(jiǎn)單的程序中使用一次泛型量不會(huì)降低羅嗦程度。但是對(duì)于多次使用泛型量的大型程序來(lái)說(shuō)可以累起來(lái)降低羅嗦程度。

          潛在的性能收益。泛型為較大的優(yōu)來(lái)可能。在泛型的初始實(shí)現(xiàn)中,編譯器將強(qiáng)類(lèi)轉(zhuǎn)換(沒(méi)有泛型的,程序會(huì)指定強(qiáng)類(lèi)轉(zhuǎn)換)插入生成的字節(jié)碼中。但是更多類(lèi)型信息可用于編譯實(shí)未來(lái)版本的 JVM 優(yōu)來(lái)可能。

          由于泛型的實(shí)現(xiàn)方式,支持泛型(幾乎)不需要 JVM 類(lèi)文件更改。所有工作都在編譯器中完成,編譯器生成類(lèi)似于沒(méi)有泛型(和強(qiáng)類(lèi)轉(zhuǎn)換時(shí)所寫(xiě)的代,只是更能確保類(lèi)型安全而已。


                                                                                     (二)

          泛型用法的例子

          泛型的許多最佳例子都來(lái)自集合框架,因?yàn)榉盒妥屇诒4嬖诩现械脑厣现付?lèi)型約束。考慮這個(gè)使用 Map 類(lèi)的例子,其中涉及一定程度的優(yōu)化,即 Map.get() 返回的結(jié)果將確實(shí)是一個(gè) String

          Map m = new HashMap();

          m.put("key", "blarg");

          String s = (String) m.get("key");

          如果有人已經(jīng)在映射中放置了不是 String 的其他東西,上面的代碼將會(huì)拋出 ClassCastException。泛型允許您表達(dá)這樣的類(lèi)型約束,即 m 是一個(gè)將 String 鍵映射到 String 值的 Map。這可以消除代碼中的強(qiáng)制類(lèi)型轉(zhuǎn)換,同時(shí)獲得一個(gè)附加的類(lèi)型檢查層,這個(gè)檢查層可以防止有人將錯(cuò)誤類(lèi)型的鍵或值保存在集合中。

          下面的代碼示例展示了 JDK 5.0 中集合框架中的 Map 接口的定義的一部分:

          public interface Map<K, V> {

           public void put(K key, V value);

           public V get(K key);

          }

          注意該接口的兩個(gè)附加物:

          類(lèi)型參數(shù) K V 在類(lèi)級(jí)別的規(guī)格說(shuō)明,表示在聲明一個(gè) Map 類(lèi)型的變量時(shí)指定的類(lèi)型的占位符。

          get()put() 和其他方法的方法簽名中使用的 K V

          為了贏得使用泛型的好處,必須在定義或?qū)嵗?/span> Map 類(lèi)型的變量時(shí)為 K V 提供具體的值。以一種相對(duì)直觀的方式做這件事:

          Map<String, String> m = new HashMap<String, String>();

          m.put("key", "blarg");

          String s = m.get("key");

          當(dāng)使用 Map 的泛型化版本時(shí),您不再需要將 Map.get() 的結(jié)果強(qiáng)制類(lèi)型轉(zhuǎn)換為 String,因?yàn)榫幾g器知道 get() 將返回一個(gè) String

          在使用泛型的版本中并沒(méi)有減少鍵盤(pán)錄入;實(shí)際上,比使用強(qiáng)制類(lèi)型轉(zhuǎn)換的版本需要做更多鍵入。使用泛型只是帶來(lái)了附加的類(lèi)型安全。因?yàn)榫幾g器知道關(guān)于您將放進(jìn) Map 中的鍵和值的類(lèi)型的更多信息,所以類(lèi)型檢查從執(zhí)行時(shí)挪到了編譯時(shí),這會(huì)提高可靠性并加快開(kāi)發(fā)速度。

          向后兼容

          Java 語(yǔ)言中引入泛型的一個(gè)重要目標(biāo)就是維護(hù)向后兼容。盡管 JDK 5.0 的標(biāo)準(zhǔn)類(lèi)庫(kù)中的許多類(lèi),比如集合框架,都已經(jīng)泛型化了,但是使用集合類(lèi)(比如 HashMap ArrayList)的現(xiàn)有代碼將繼續(xù)不加修改地在 JDK 5.0 中工作。當(dāng)然,沒(méi)有利用泛型的現(xiàn)有代碼將不會(huì)贏得泛型的類(lèi)型安全好處。


                                                                                 (三)

          類(lèi)型參數(shù)

          在定泛型類(lèi)或聲明泛型類(lèi)時(shí),使用尖括號(hào)來(lái)指定形式類(lèi)型參數(shù)。形式類(lèi)型參數(shù)與實(shí)際類(lèi)型參數(shù)之關(guān)類(lèi)似于形式方法參數(shù)與實(shí)際方法參數(shù)之關(guān)系,只是類(lèi)型參數(shù)表示類(lèi)型,而不是表示

          泛型類(lèi)中的類(lèi)型參數(shù)幾乎可以用于任何可以使用類(lèi)名的地方。例如,下面是 java.util.Map 接口的定的摘

          public interface Map<K, V> {

           public void put(K key, V value);

           public V get(K key);

          }

          Map 接口是由兩個(gè)類(lèi)型參數(shù)化的,兩個(gè)類(lèi)型是鍵類(lèi) K 值類(lèi) V。(不使用泛型)將會(huì)接受或返回 Object 的方法現(xiàn)在在它的方法名中使用 K V,指示附加的類(lèi)束位于 Map 規(guī)說(shuō)明之下。

          當(dāng)聲明或者實(shí)例化一個(gè)泛型的對(duì)時(shí),必指定類(lèi)型參數(shù)的

          Map<String, String> map = new HashMap<String, String>();

          注意,在本例中,必指定兩次類(lèi)型參數(shù)。一次是在聲明 map 類(lèi)時(shí),另一次是在選擇 HashMap 類(lèi)的參數(shù)化以便可以實(shí)例化正確類(lèi)型的一個(gè)實(shí)時(shí)

          編譯器在遇到一個(gè) Map<String, String> 類(lèi)型的時(shí),知道 K V 現(xiàn)在被 String,因此它知道在這樣量上調(diào) Map.get() 將會(huì)得到 String 類(lèi)型。

          除了異常類(lèi)型、枚或匿名內(nèi)部類(lèi)以外,任何類(lèi)都可以具有類(lèi)型參數(shù)。

          命名類(lèi)型參數(shù)

          推薦的命名定是使用大寫(xiě)的個(gè)字母名稱作為類(lèi)型參數(shù)。 C++ 定有所不同(參 A:與 C++ 模板的比),并反映了大多數(shù)泛型類(lèi)將具有少量類(lèi)型參數(shù)的假定。對(duì)于常見(jiàn)的泛型模式,推薦的名稱是:

          K —— ,比如映射的

          V —— ,比如 List Set 的內(nèi)容,或者 Map 中的

          E —— 異常類(lèi)

          T ——泛型。

          泛型不是協(xié)變

          關(guān)于泛型的混淆,一個(gè)常見(jiàn)的來(lái)源就是假設(shè)像數(shù)協(xié)變的。其實(shí)不是協(xié)變的。List<Object> 不是 List<String> 的父類(lèi)型。

          如果 A 擴(kuò) B,那 A 的數(shù)也是 B 的數(shù),并且完全可以在需要 B[] 的地方使用 A[]

          Integer[] intArray = new Integer[10]; 

          Number[] numberArray = intArray;

          上面的代是有效的,因一個(gè) Integer 一個(gè) Number,因而一個(gè) Integer 數(shù)一個(gè) Number 數(shù)。但是對(duì)于泛型來(lái)說(shuō)則不然。下面的代是無(wú)效的:

          List<Integer> intList = new ArrayList<Integer>();

          List<Number> numberList = intList; // invalid

          最初,大多數(shù) Java 程序員覺(jué)缺少協(xié)變人,或者甚至是“壞的(broken)”,但是之所以這樣有一個(gè)很好的原因。如果可以將 List<Integer> 賦給 List<Number>,下面的代就會(huì)背泛型應(yīng)該提供的類(lèi)型安全:

          List<Integer> intList = new ArrayList<Integer>();

          List<Number> numberList = intList; // invalid

          numberList.add(new Float(3.1415));

          intList numberList 都是有名的,如果允,上面的代就會(huì)您將不是 Integers 西放進(jìn) intList 中。但是,正如下一屏將會(huì)看到的,您有一個(gè)更加靈活的方式來(lái)定泛型。


                                                                                         (四)

          類(lèi)型通配符

          設(shè)您具有方法:

          void printList(List l) {

           for (Object o : l)

              System.out.println(o);

          }

          上面的代 JDK 5.0 編譯過(guò),但是如果試圖 List<Integer> 調(diào)用它,會(huì)得到警告。現(xiàn)警告是因,您將泛型(List<Integer>傳遞給一個(gè)只承將它當(dāng)作 List(所的原始類(lèi)型)的方法,將破壞使用泛型的類(lèi)型安全。

          如果試圖編寫(xiě)像下面這樣的方法,那將會(huì)怎么樣

          void printList(List<Object> l) {

           for (Object o : l)

              System.out.println(o);

          }

          它仍然不會(huì)通過(guò)編譯,因一個(gè) List<Integer> 不是一個(gè) List<Object>(正如前一屏泛型不是協(xié)變中所學(xué)的)。才真正 —— 現(xiàn)在您的泛型版本沒(méi)有普通的非泛型版本有用!

          解決方案是使用類(lèi)型通配符:

          void printList(List<?> l) {

           for (Object o : l)

              System.out.println(o);

          }

          上面代中的問(wèn)號(hào)是一個(gè)類(lèi)型通配符。它作“問(wèn)號(hào)”。List<?> 是任何泛型 List 的父類(lèi)型,所以您完全可以將 List<Object>List<Integer> List<List<List<Flutzpah>>> 傳遞給 printList()

          類(lèi)型通配符的作用

          類(lèi)型通配符中引入了類(lèi)型通配符,這讓您可以聲明 List<?> 類(lèi)型的量。您可以對(duì)這樣 List 做什呢?非常方便,可以從中索元素,但是不能添加元素。原因不是編譯器知道哪些方法修改列表哪些方法不修改列表,而是(大多數(shù))化的方法比不化的方法需要更多的類(lèi)型信息。下面的代碼則工作得很好:

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

          li.add(new Integer(42));

          List<?> lu = li;

          System.out.println(lu.get(0));

          么該能工作呢?對(duì) lu編譯器一點(diǎn)都不知道 List 類(lèi)型參數(shù)的。但是編譯器比較聰明,它可以做一些類(lèi)型推理。在本例中,它推斷未知的類(lèi)型參數(shù)必須擴(kuò) Object。(個(gè)特定的推理沒(méi)有太大的跳,但是編譯器可以作出一些非常令人佩服的類(lèi)型推理,后面就會(huì)看到(在層細(xì)節(jié)節(jié)中)。所以它調(diào) List.get() 并推斷返回類(lèi) Object

          另一方面,下面的代不能工作:

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

          li.add(new Integer(42));

          List<?> lu = li;

          lu.add(new Integer(43)); // error

          在本例中,對(duì) lu編譯器不能對(duì) List 類(lèi)型參數(shù)作出足夠嚴(yán)密的推理,以確定將 Integer 傳遞給 List.add() 類(lèi)型安全的。所以編譯器將不允這么做。

          以免您仍然認(rèn)為編譯器知道哪些方法更改列表的內(nèi)容哪些不更改列表內(nèi)容,請(qǐng)注意下面的代將能工作,因它不依編譯器必知道關(guān) lu 類(lèi)型參數(shù)的任何信息:

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

          li.add(new Integer(42));

          List<?> lu = li;

          lu.clear();

          泛型方法 

          (在類(lèi)型參數(shù)節(jié)中)您已經(jīng)看到,通過(guò)類(lèi)的定中添加一個(gè)形式類(lèi)型參數(shù)列表,可以將類(lèi)泛型化。方法也可以被泛型化,不管它在其中的類(lèi)是不是泛型化的。

          泛型類(lèi)在多個(gè)方法間實(shí)類(lèi)束。在 List<V> 中,類(lèi)型參數(shù) V 現(xiàn) get()add()contains() 等方法的名中。當(dāng)創(chuàng)建一個(gè) Map<K, V> 類(lèi)型的時(shí),您就在方法之宣稱一個(gè)類(lèi)束。您傳遞給 add() 將與 get() 返回的類(lèi)型相同。

          類(lèi)似地,之所以聲明泛型方法,一般是因您想要在方法的多個(gè)參數(shù)之宣稱一個(gè)類(lèi)。例如,下面代中的 ifThenElse() 方法,根據(jù)它的第一個(gè)參數(shù)的布爾值,它將返回第二個(gè)或第三個(gè)參數(shù):

          public <T> T ifThenElse(boolean b, T first, T second) {

           return b ? first : second;

          }

          注意,您可以調(diào) ifThenElse(),而不用式地告訴編譯器,您想要 T 的什么值編譯器不必式地被告知 T 將具有什么值;它只知道都必相同。編譯器允調(diào)用下面的代,因為編譯器可以使用類(lèi)型推理來(lái)推斷出,替 T String 滿足所有的類(lèi)束:

          String s = ifThenElse(b, "a", "b");

          類(lèi)似地,您可以調(diào)用:

          Integer i = ifThenElse(b, new Integer(1), new Integer(2));

          但是,編譯器不允下面的代,因沒(méi)有類(lèi)型會(huì)滿足所需的類(lèi)束:

          String s = ifThenElse(b, "pi", new Float(3.14));

          選擇使用泛型方法,而不是將類(lèi) T 添加到類(lèi)呢?(至少)有兩情況應(yīng)該這樣做:

          當(dāng)泛型方法是靜態(tài)時(shí)這種情況下不能使用類(lèi)類(lèi)型參數(shù)。

          當(dāng) T 上的類(lèi)對(duì)于方法真正是局部的時(shí)意味著沒(méi)有在相同類(lèi)的另一個(gè)方法名中使用相同類(lèi) T 束。通過(guò)使得泛型方法的類(lèi)型參數(shù)對(duì)于方法是局部的,可以簡(jiǎn)化封閉類(lèi)型的名。

          有限制類(lèi)

          在前面泛型方法的例子中,類(lèi)型參數(shù) V 是無(wú)束的或無(wú)限制的類(lèi)型。有時(shí)沒(méi)有完全指定類(lèi)型參數(shù)時(shí),需要對(duì)類(lèi)型參數(shù)指定附加的束。

          例子 Matrix 類(lèi),它使用類(lèi)型參數(shù) V參數(shù)由 Number 類(lèi)來(lái)限制:

          public class Matrix<V extends Number> { ... }

          編譯器允創(chuàng) Matrix<Integer> Matrix<Float> 類(lèi)型的量,但是如果您試圖 Matrix<String> 類(lèi)型的量,會(huì)出現(xiàn)錯(cuò)誤類(lèi)型參數(shù) V 被判斷 Number 限制在沒(méi)有類(lèi)型限制時(shí),假設(shè)類(lèi)型參數(shù)由 Object 限制。就是前一屏泛型方法中的例子,允 List.get() List<?> 調(diào)時(shí)返回 Object,即使編譯器不知道類(lèi)型參數(shù) V 類(lèi)
          posted on 2008-10-24 09:35 蘆葦 閱讀(509) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): JAVA
          主站蜘蛛池模板: 南木林县| 兴业县| 红原县| 中西区| 定南县| 鹤庆县| 库尔勒市| 上蔡县| 大城县| 桑植县| 永福县| 边坝县| 德安县| 防城港市| 嘉峪关市| 五台县| 大竹县| 寿光市| 开平市| 江门市| 泰和县| 新宾| 榆中县| 凌云县| 亳州市| 喀喇| 罗山县| 介休市| 旺苍县| 西城区| 鄂尔多斯市| 芒康县| 安泽县| 吴川市| 昌宁县| 灵寿县| 馆陶县| 宿州市| 宁国市| 青阳县| 临朐县|