VIRGIN FOREST OF JAVA
          不要埋頭苦干,要學(xué)習(xí),學(xué)習(xí),再學(xué)習(xí)。。。。。
          powered by R.Zeus

          原文:http://www.onjava.com/pub/a/onjava/2005/07/06/generics.html
          作者:Budi Kurniawan
          翻譯:di_feng_ro@hotmail.com

               泛型是J2SE 5.0最重要的特性。他們讓你寫一個(gè)type(類或接口)和創(chuàng)建一個(gè)實(shí)例通過(guò)傳遞一個(gè)或多個(gè)引用類型。這個(gè)實(shí)例受限于只能作用于這些類型。比如,在java 5,java.util.List 已經(jīng)被泛化。當(dāng)建立一個(gè)list對(duì)象時(shí),你通過(guò)傳遞一個(gè)java類型建立一個(gè)List實(shí)例,此list實(shí)例只能作用于所傳遞的類型。這意味著如果你傳遞一個(gè)String ,此List實(shí)例只能擁有String對(duì)象;如果你傳遞一個(gè)Integer,此實(shí)例只能存貯Integer對(duì)象。除了創(chuàng)建參數(shù)化的類型,你還能創(chuàng)建參數(shù)化的函數(shù)。
               泛型的第一個(gè)好處是編譯時(shí)的嚴(yán)格類型檢查。這是Collections framework最重要的特點(diǎn)。此外,泛型消除了絕大多數(shù)的類型轉(zhuǎn)換。在JDK 5.0之前,當(dāng)你使用Collections framework時(shí),你不得不進(jìn)行類型轉(zhuǎn)換。
               本文將教你如何操作泛型類型。它的第一部分是“沒(méi)有泛型的日子”,先讓我們回憶老版本JDK的不便。然后,舉一些泛型的例子。在討論完語(yǔ)法以及有界泛型的使用之后,文章最后一章將解釋如何寫泛型。

           


            沒(méi)有泛型的日子

               所有的java類都源自java.lang.Object,這意味著所有的JAVA對(duì)象能轉(zhuǎn)換成Object。因此,在之前的JDK的版本中,很多Collections framework的函數(shù)接受一個(gè)Object參數(shù)。所以,collections是一個(gè)能持有任何對(duì)象的多用途工具,但帶來(lái)了不良的后果。
               舉個(gè)簡(jiǎn)單的例子,在JDK 5.0的之前版本中,類List的函數(shù)add接受一個(gè)Object參數(shù):

           public boolean add(java.lang.Object element)

           所以你能傳遞任何類型給add。這是故意這么設(shè)計(jì)的。否則,它只能傳遞某種特定的對(duì)象,這樣就會(huì)出現(xiàn)各種List類型,如,StringList, EmployeeList, AddressList等。
               add通過(guò)Object傳遞能帶來(lái)好處,現(xiàn)在我們考慮get函數(shù)(返回List中的一個(gè)元素).如下是JDK 5之前版本的定義:
           
          public java.lang.Object get(int index) throws IndexOutOfBoundsException

           
          get返回一個(gè)Object.不幸的事情從此開(kāi)始了.假如你儲(chǔ)存了兩個(gè)String對(duì)象在一個(gè)List中:

          List stringList1 = new ArrayList();
          stringList1.add("Java 5");
          stringList1.add("with generics");

          當(dāng)你想從stringList1取得一個(gè)元素時(shí),你得到了一個(gè)Object.為了操作原來(lái)的類型元素,你不得不把它轉(zhuǎn)換String。

          String s1 = (String) stringList1.get(0);

          但是,假如你曾經(jīng)把一個(gè)non-String對(duì)象加入stringList1中,上面的代碼會(huì)拋出一個(gè)ClassCastException.
             有了泛型,你能創(chuàng)建一個(gè)單一用途的List實(shí)例.比如,你能創(chuàng)建一個(gè)只接受String對(duì)象的List實(shí)例,另外一個(gè)實(shí)例只能接受Employee對(duì)象.這同樣適用于Collections framework中的其他類型.

           

          泛型入門


             像一個(gè)函數(shù)能接受參數(shù)一樣,一個(gè)泛型類也能接受參數(shù).這就是一個(gè)泛型類經(jīng)常被稱為一個(gè)parameterized type的原因.但是不像函數(shù)用()傳遞參數(shù),泛型類是用<>傳遞參數(shù)的.聲明一個(gè)泛型類和聲明一個(gè)普通類沒(méi)有什么區(qū)別,只不過(guò)你把泛型的變量放在<>中.
             比如,在JDK 5中,你可以這樣聲明一個(gè)java.util.List :  List<E> myList;E 稱為type variable.意味著一個(gè)變量將被一個(gè)類型替代.替代type variable的值將被當(dāng)作參數(shù)或返回類型.對(duì)于List接口來(lái)說(shuō),當(dāng)一個(gè)實(shí)例被創(chuàng)建以后,E 將被當(dāng)作一個(gè)add或別的函數(shù)的參數(shù).E 也會(huì)使get或別的參數(shù)的返回值.下面是add和get的定義:

          boolean add<E o>
          E get(int index)

          NOTE:一個(gè)泛型在聲明或例示時(shí)允許你傳遞特定的type variable: E.除此之外,如果E是個(gè)類,你可以傳遞子類;如果E是個(gè)接口,你可以傳遞實(shí)現(xiàn)接口的類;

          -----------------------------譯者添加--------------------
           List<Number> numberList= new ArrayList<Number>();
             numberList.add(2.0);
             numberList.add(2);
          -----------------------------譯者添加--------------------

          如果你傳遞一個(gè)String給一個(gè)List,比如:

          List<String> myList;

          那么mylist的add函數(shù)將接受一個(gè)String作為他的參數(shù),而get函數(shù)將返回一個(gè)String.因?yàn)榉祷亓艘粋€(gè)特定的類型,所以不用類型轉(zhuǎn)化了。

          NOTE:根據(jù)慣例,我們使用一個(gè)唯一的大寫字目表示一個(gè)type variable。為了創(chuàng)建一個(gè)泛型類,你需在聲明時(shí)傳遞同樣的參數(shù)列表。比如,你要想創(chuàng)建一個(gè)ArrayList來(lái)操作String ,你必須把String放在<>
          中。如:

          List<String> myList = new ArrayList<String>();

          再比如,java.util.Map 是這么定義的:

          public interface Map<K,V>

          K用來(lái)聲明map密鑰(KEY)的類型而V用來(lái)表示值(VALUE)的類型。put和values是這么定義的:

          V put(K key, V value)
          Collection<V> values()

          NOTE:一個(gè)泛型類不準(zhǔn)直接的或間接的是java.lang.Throwable的子類。因?yàn)楫惓J窃趓un time拋出的,所以它不可能預(yù)言什么類型的異常將在compile time拋出.

          列表1的例子將比較List在JDK 1.4 和JDK1.5的不同

          package com.brainysoftware.jdk5.app16;

          import java.util.List;

          import java.util.ArrayList;

           

          public class GenericListTest {

            public static void main(String[] args) {

              // in JDK 1.4

              List stringList1 = new ArrayList();

              stringList1.add("Java 1.0 - 5.0");

              stringList1.add("without generics");

              // cast to java.lang.String

              String s1 = (String) stringList1.get(0);

              System.out.println(s1.toUpperCase());

           

              // now with generics in JDK 5

              List<String> stringList2 = new ArrayList<String>();

              stringList2.add("Java 5.0");

              stringList2.add("with generics");

              // no need for type casting

              String s2 = stringList2.get(0);

              System.out.println(s2.toUpperCase());

            }

          }

          在列表1中,stringList2是個(gè)泛型類。聲明List<String>告訴編譯器List的實(shí)例能接受一個(gè)String對(duì)象。當(dāng)然,在另外的情況中,你能新建能接受各種對(duì)象的List實(shí)例。注意,當(dāng)從List實(shí)例中返回成員元素時(shí),不需要對(duì)象轉(zhuǎn)化,因?yàn)樗祷氐牧四阆胍念愋停簿褪荢tring.

          NOTE:泛型的類型檢查(type checking)是在compile time完成的.

                最讓人感興趣的事情是,一個(gè)泛型類型是個(gè)類型并且能被當(dāng)作一個(gè)type variable。比如,你想你的List儲(chǔ)存lists of Strings,你能通過(guò)把List<String>作為他的type variable來(lái)聲明List。比如:

          List<List<String>> myListOfListsOfStrings;

          要從myList中的第一個(gè)List重新取得String,你可以這么用:

          String s = myListOfListsOfStrings.get(0).get(0);

          下一個(gè)列表中的ListOfListsTest類示范了一個(gè)List(命名為listOfLists)接受一個(gè)String List作為參數(shù)。
          package com.brainysoftware.jdk5.app16;

          import java.util.ArrayList;

          import java.util.List;

          public class ListOfListsTest {

            public static void main(String[] args) {

              List<String> listOfStrings = new ArrayList<String>();

              listOfStrings.add("Hello again");

              List<List<String>> listOfLists = new ArrayList<List<String>>();

              listOfLists.add(listOfStrings);

              String s = listOfLists.get(0).get(0);

              System.out.println(s); // prints "Hello again"

            }

          }

          另外,一個(gè)泛型類型接受一個(gè)或多個(gè)type variable。比如,java.util.Map有兩個(gè)type variables。第一個(gè)定義了密鑰(key)的類型,第二個(gè)定義了值(value)的類型。下面的例子講教我們?nèi)绾问褂脗€(gè)一個(gè)泛型Map.
          package com.brainysoftware.jdk5.app16;

          import java.util.HashMap;

          import java.util.Map;

          public class MapTest {

            public static void main(String[] args) {

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

              map.put("key1", "value1");

              map.put("key2", "value2");

              String value1 = map.get("key1");

            }

          }
          在這個(gè)例子中,重新得到一個(gè)key1代表的String值,我們不需要任何類型轉(zhuǎn)換。

           

          沒(méi)有參數(shù)的情況下使用泛型


              既然在J2SE 5.0中收集類型已經(jīng)泛型化,那么,原來(lái)的使用這些類型的代碼將如何呢?很幸運(yùn),他們?cè)贘AVA 5中將繼續(xù)工作,因?yàn)槟隳苁褂脹](méi)有參數(shù)的泛型類型。比如,你能繼續(xù)像原來(lái)一樣使用List接口,正如下面的例子一樣。

          List stringList1 = new ArrayList();
          stringList1.add("Java 1.0 - 5.0");
          stringList1.add("without generics");
          String s1 = (String) stringList1.get(0);


          一個(gè)沒(méi)有任何參數(shù)的泛型類型被稱為raw type。它意味著這些為JDK1.4或更早的版本而寫的代碼將繼續(xù)在java 5中工作。

          盡管如此,一個(gè)需要注意的事情是,JDK5編譯器希望你使用帶參數(shù)的泛型類型。否則,編譯器將提示警告,因?yàn)樗J(rèn)為你可能忘了定義type variables。比如,編譯上面的代碼的時(shí)候你會(huì)看到下面這些警告,因?yàn)榈谝粋€(gè)List被認(rèn)為是 raw type。

          Note: com/brainysoftware/jdk5/app16/GenericListTest.java
                  uses unchecked or unsafe operations.
          Note: Recompile with -Xlint:unchecked for details.


          當(dāng)你使用raw type時(shí),如果你不想看到這些警告,你有幾個(gè)選擇來(lái)達(dá)到目的:

          1.編譯時(shí)帶上參數(shù)-source 1.4
          2.使用@SupressWarnings("unchecked")注釋
          3.更新你的代碼,使用List<Object>. List<Object>的實(shí)例能接受任何類型的對(duì)象,就像是一個(gè)raw type List。然而,編譯器不會(huì)發(fā)脾氣。

           

          使用 ? 通配符

             前面提過(guò),如果你聲明了一個(gè)List<aType>, 那么這個(gè)List對(duì)aType起作用,所以你能儲(chǔ)存下面這些類型的對(duì)象:
          1.一個(gè)aType的實(shí)例
          2.它的子類的實(shí)例(如果aType是個(gè)類)
          3.實(shí)現(xiàn)aType接口的類實(shí)例(如果aType是個(gè)接口)

          但是,請(qǐng)注意,一個(gè)泛型本身是個(gè)JAVA類型,就像java.lang.String或java.io.File一樣。傳遞不同的type variable給泛型可以創(chuàng)建不同的JAVA類型。比如,下面例子中l(wèi)ist1和list2引用了不同的類型對(duì)象。

          List<Object> list1 = new ArrayList<Object>();
          List<String> list2 = new ArrayList<String>();


          list1指向了一個(gè)type variables為java.lang.Objects 的List而list2指向了一個(gè)type variables為String 的List。所以傳遞一個(gè)List<String>給一個(gè)參數(shù)為L(zhǎng)ist<Object>的函數(shù)將導(dǎo)致compile time錯(cuò)誤。下面列表可以說(shuō)明:

          package com.brainysoftware.jdk5.app16;
          import java.util.ArrayList;
          import java.util.List;
           
          public class AllowedTypeTest {
            public static void doIt(List<Object> l) {
            }
            public static void main(String[] args) {
              List<String> myList = new ArrayList<String>();
              // 這里將產(chǎn)生一個(gè)錯(cuò)誤
              doIt(myList);
            }
          }

          上面的代碼無(wú)法編譯,因?yàn)槟阍噲D傳遞一個(gè)錯(cuò)誤的類型給函數(shù)doIt。doIt的參數(shù)是List<Object>二你傳遞的參數(shù)是List<String>。

          可以使用 ? 通配符解決這個(gè)難題。List<?> 意味著一個(gè)對(duì)任何對(duì)象起作用的List。所以,doIt可以改為:
           
          public static void doIt(List<?> l) {}

              在某些情況下你會(huì)考慮使用 ? 通配符。比如,你有一個(gè)printList函數(shù),這個(gè)函數(shù)打印一個(gè)List的所有成員,你想讓這個(gè)函數(shù)對(duì)任何類型的List起作用時(shí)。否則,你只能累死累活的寫很多printList的重載函數(shù)。下面的列表引用了使用 ? 通配符的printList函數(shù)。

          package com.brainysoftware.jdk5.app16;
          import java.util.ArrayList;
          import java.util.List;
           
          public class WildCardTest {
           
            public static void printList(List<?> list) {
              for (Object element : list) {
                System.out.println(element);
              }
            }
            public static void main(String[] args) {
              List<String> list1 = new ArrayList<String>();
              list1.add("Hello");
              list1.add("World");
              printList(list1);
           
              List<Integer> list2 = new ArrayList<Integer>();
              list2.add(100);
              list2.add(200);
              printList(list2);
            }
          }
          這些代碼說(shuō)明了在printList函數(shù)中,List<?>表示各種類型的List對(duì)象。然而,請(qǐng)注意,在聲明的時(shí)候使用 ? 通配符是不合法的,像這樣:

          List<?> myList = new ArrayList<?>(); // 不合法

          如果你想創(chuàng)建一個(gè)接收任何類型對(duì)象的List,你可以使用Object作為type variable,就像這樣:

          List<Object> myList = new ArrayList<Object>();


          在函數(shù)中使用界限通配符

          在之前的章節(jié)中,你學(xué)會(huì)了通過(guò)傳遞不同的type variables來(lái)創(chuàng)建不同JAVA類型的泛型,但并不考慮type variables之間的繼承關(guān)系。在很多情況下,你想一個(gè)函數(shù)有不同的List參數(shù)。比如,你有一個(gè)函數(shù)getAverage,他返回了一個(gè)List中成員的平均值。然而,如果你把List<Number>作為etAverage的參數(shù),你就沒(méi)法傳遞List<Integer> 或List<Double>參數(shù),因?yàn)長(zhǎng)ist<Number>和List<Integer> 和List<Double>不是同樣的類型。你能使用raw type 或使用通配符,但這樣無(wú)法在compile time進(jìn)行安全類型檢查,因?yàn)槟隳軅鬟f任何任何類型的List,比如List<String>的實(shí)例。你可以使用List<Number>作為參數(shù),但是你就只能傳遞List<Number>給函數(shù)。但這樣就使你的函數(shù)功能減少,因?yàn)槟憧赡芨嗟臅r(shí)候要操作List<Integer>或List<Long>,而不是List<Number>。

          J2SE5.0增加了一個(gè)規(guī)則來(lái)解決了這種約束,這個(gè)規(guī)則就是允許你定義一個(gè)上界(upper bound) type variable.在這種方式中,你能傳遞一個(gè)類型或它的子類。在上面getAverage函數(shù)的例子中,你能傳遞一個(gè)List<Number>或它的子類的實(shí)例,比如List<Integer> or List<Float>。

          使用上界規(guī)則的語(yǔ)法這么定義的:GenericType<? extends upperBoundType>. 比如,對(duì)getAverage函數(shù)的參數(shù),你可以這么寫List<? extends Number>. 下面例子說(shuō)明了如何使用這種規(guī)則。

          package com.brainysoftware.jdk5.app16;
          import java.util.ArrayList;
          import java.util.List;
          public class BoundedWildcardTest {
            public static double getAverage(List<? extends Number> numberList)
            {
              double total = 0.0;
              for (Number number : numberList)
                total += number.doubleValue();
              return total/numberList.size();
            }
           
            public static void main(String[] args) {
              List<Integer> integerList = new ArrayList<Integer>();
              integerList.add(3);
              integerList.add(30);
              integerList.add(300);
              System.out.println(getAverage(integerList)); // 111.0
              List<Double> doubleList = new ArrayList<Double>();
              doubleList.add(3.0);
              doubleList.add(33.0);
              System.out.println(getAverage(doubleList)); // 18.0
            }
          }
          由于有了上界規(guī)則,上面例子中的getAverage函數(shù)允許你傳遞一個(gè)List<Number> 或一個(gè)type variable是任何java.lang.Number子類的List。


          下界規(guī)則

          關(guān)鍵字extends定義了一個(gè)type variable的上界。通過(guò)使用super關(guān)鍵字,我們可以定義一個(gè)type variable的下界,盡管通用的情況不多。比如,如果一個(gè)函數(shù)的參數(shù)是List<? super Integer>,那么意味著你可以傳遞一個(gè)List<Integer>的實(shí)例或者任何java.lang.Integer的超類(superclass)。


          創(chuàng)建泛型類


          前面的章節(jié)主要說(shuō)明了如何使使用泛型類,特別是Collections framework中的類。現(xiàn)在我們開(kāi)始學(xué)習(xí)如何寫自己的泛型類。

          基本上,除了聲明一些你想要使用的type variables外,一個(gè)泛型類和別的類沒(méi)有什么區(qū)別。這些type variables位于類型后面的<>中。比如,下面的Point就是個(gè)泛型類。一個(gè)Point對(duì)象代表了一個(gè)系統(tǒng)中的點(diǎn),它有橫坐標(biāo)和縱坐標(biāo)。通過(guò)使Point泛型化,你能定義一個(gè)點(diǎn)實(shí)例的精確程度。比如,一個(gè)Point對(duì)象需要非常精確,你能把Double作為type variable。否則,Integer 就夠了。

          package com.brainysoftware.jdk5.app16;
          public class Point<T> {
            T x;
            T y;
            public Point(T x, T y) {
              this.x = x;
              this.y = y;
            }
            public T getX() {
              return x;
            }
            public T getY() {
              return y;
            }
            public void setX(T x) {
              this.x = x;
            }
            public void setY(T y) {
              this.y = y;
            }
          }

          在這個(gè)例子中,T是Point的type variable 。T是getX和getY的返回值類型,也是setX和setY的參數(shù)類型。此外,構(gòu)造函數(shù)結(jié)合兩個(gè)T參數(shù)。

          使用point類就像使用別的類一樣。比如,下面的例子創(chuàng)建了兩個(gè)Point對(duì)象:ponint1和point2。前者把Integer作為type variable,而后者把Double作為type variable。

          Point<Integer> point1 = new Point<Integer>(4, 2);
          point1.setX(7);
          Point<Double> point2 = new Point<Double>(1.3, 2.6);
          point2.setX(109.91);

          總結(jié)

          泛型使代碼在compile time有了更嚴(yán)格的類型檢查。特別是在Collections framework中,泛型有兩個(gè)作用。第一,他們?cè)黾恿藢?duì)收集類型(collection types)在compile time的類型檢查,所以收集類所能持有的類型對(duì)傳遞給它的參數(shù)類型起了限制作用。比如你創(chuàng)建了一個(gè)持有strings的java.util.List實(shí)例,那么他就將不能接受Integers或別的類型。其次,當(dāng)你從一個(gè)收集中取得一個(gè)元素時(shí),泛型消除了類型轉(zhuǎn)換的必要。

          泛型能夠在沒(méi)有type variable的情況下使用,比如,作為raw types。這些措施讓Java 5之前的代碼能夠運(yùn)行在JRE 5中。但是,對(duì)新的應(yīng)用程序,你最好不要使用raw types,因?yàn)橐院驤ava可能不支持他們。


          你已經(jīng)知道通過(guò)傳遞不同類型的type variable給泛型類可以產(chǎn)生不同的JAVA類型。就是說(shuō)List<String>和List<Object>的類型是不同的。盡管String是java.lang.Object。但是傳遞一個(gè)List<String>給一個(gè)參數(shù)是List<Object>的函數(shù)會(huì)參數(shù)會(huì)產(chǎn)生編譯錯(cuò)誤(compile error)。函數(shù)能用 ? 通配符使其接受任何類型的參數(shù)。List<?> 意味著任何類型的對(duì)象。

          最后,你已經(jīng)看到了寫一個(gè)泛型類和別的一般JAVA類沒(méi)有什么區(qū)別。你只需要在類型名稱后面的<>中聲明一系列的type variables就行了。這些type variables就是返回值類型或者參數(shù)類型。根據(jù)慣例,一個(gè)
          type variable用一個(gè)大寫字母表示。

          posted on 2005-08-17 21:56 R.Zeus 閱讀(419) 評(píng)論(1)  編輯  收藏 所屬分類: J2SE

          FeedBack:
          # re: Generics in J2SE 5.0(翻譯)
          2015-11-24 15:29 | bhjbgj
          package com.test.j2se;

          public class TestThread1 {

          public static void main(String[] args) {
          // TODO Auto-generated method stub
          for(int i=0;i<=10;i++){
          System.out.println("Main Thread ----"+i);
          }
          T2 t1=new T2();
          t1.start();
          }

          }
          class T2 extends Thread{
          public void run(){
          for(int i=0;i<10;i++){
          System.out.println("T Thread ----"+i);
          }
          }
          }
            回復(fù)  更多評(píng)論
            
          主站蜘蛛池模板: 平潭县| 中超| 漳浦县| 大姚县| 牟定县| 绍兴市| 玛多县| 齐河县| 华池县| 新密市| 年辖:市辖区| 临澧县| 南宫市| 临桂县| 台南市| 云林县| 永和县| 连南| 绥宁县| 遵义市| 通州市| 辽宁省| 乌拉特后旗| 鄂尔多斯市| 井陉县| 清涧县| 九龙坡区| 新建县| 庆城县| 保定市| 郎溪县| 靖远县| 南安市| 内黄县| 大竹县| 定襄县| 襄汾县| 霍林郭勒市| 安义县| 漳浦县| 依安县|