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

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

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

    VIRGIN FOREST OF JAVA
    不要埋頭苦干,要學習,學習,再學習。。。。。
    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最重要的特性。他們讓你寫一個type(類或接口)和創建一個實例通過傳遞一個或多個引用類型。這個實例受限于只能作用于這些類型。比如,在java 5,java.util.List 已經被泛化。當建立一個list對象時,你通過傳遞一個java類型建立一個List實例,此list實例只能作用于所傳遞的類型。這意味著如果你傳遞一個String ,此List實例只能擁有String對象;如果你傳遞一個Integer,此實例只能存貯Integer對象。除了創建參數化的類型,你還能創建參數化的函數。
         泛型的第一個好處是編譯時的嚴格類型檢查。這是Collections framework最重要的特點。此外,泛型消除了絕大多數的類型轉換。在JDK 5.0之前,當你使用Collections framework時,你不得不進行類型轉換。
         本文將教你如何操作泛型類型。它的第一部分是“沒有泛型的日子”,先讓我們回憶老版本JDK的不便。然后,舉一些泛型的例子。在討論完語法以及有界泛型的使用之后,文章最后一章將解釋如何寫泛型。

     


      沒有泛型的日子

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

     public boolean add(java.lang.Object element)

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

     
    get返回一個Object.不幸的事情從此開始了.假如你儲存了兩個String對象在一個List中:

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

    當你想從stringList1取得一個元素時,你得到了一個Object.為了操作原來的類型元素,你不得不把它轉換String。

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

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

     

    泛型入門


       像一個函數能接受參數一樣,一個泛型類也能接受參數.這就是一個泛型類經常被稱為一個parameterized type的原因.但是不像函數用()傳遞參數,泛型類是用<>傳遞參數的.聲明一個泛型類和聲明一個普通類沒有什么區別,只不過你把泛型的變量放在<>中.
       比如,在JDK 5中,你可以這樣聲明一個java.util.List :  List<E> myList;E 稱為type variable.意味著一個變量將被一個類型替代.替代type variable的值將被當作參數或返回類型.對于List接口來說,當一個實例被創建以后,E 將被當作一個add或別的函數的參數.E 也會使get或別的參數的返回值.下面是add和get的定義:

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

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

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

    如果你傳遞一個String給一個List,比如:

    List<String> myList;

    那么mylist的add函數將接受一個String作為他的參數,而get函數將返回一個String.因為返回了一個特定的類型,所以不用類型轉化了。

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

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

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

    public interface Map<K,V>

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

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

    NOTE:一個泛型類不準直接的或間接的是java.lang.Throwable的子類。因為異常是在run time拋出的,所以它不可能預言什么類型的異常將在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是個泛型類。聲明List<String>告訴編譯器List的實例能接受一個String對象。當然,在另外的情況中,你能新建能接受各種對象的List實例。注意,當從List實例中返回成員元素時,不需要對象轉化,因為他返回的了你想要的類型,也就是String.

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

          最讓人感興趣的事情是,一個泛型類型是個類型并且能被當作一個type variable。比如,你想你的List儲存lists of Strings,你能通過把List<String>作為他的type variable來聲明List。比如:

    List<List<String>> myListOfListsOfStrings;

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

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

    下一個列表中的ListOfListsTest類示范了一個List(命名為listOfLists)接受一個String List作為參數。
    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"

      }

    }

    另外,一個泛型類型接受一個或多個type variable。比如,java.util.Map有兩個type variables。第一個定義了密鑰(key)的類型,第二個定義了值(value)的類型。下面的例子講教我們如何使用個一個泛型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");

      }

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

     

    沒有參數的情況下使用泛型


        既然在J2SE 5.0中收集類型已經泛型化,那么,原來的使用這些類型的代碼將如何呢?很幸運,他們在JAVA 5中將繼續工作,因為你能使用沒有參數的泛型類型。比如,你能繼續像原來一樣使用List接口,正如下面的例子一樣。

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


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

    盡管如此,一個需要注意的事情是,JDK5編譯器希望你使用帶參數的泛型類型。否則,編譯器將提示警告,因為他認為你可能忘了定義type variables。比如,編譯上面的代碼的時候你會看到下面這些警告,因為第一個List被認為是 raw type。

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


    當你使用raw type時,如果你不想看到這些警告,你有幾個選擇來達到目的:

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

     

    使用 ? 通配符

       前面提過,如果你聲明了一個List<aType>, 那么這個List對aType起作用,所以你能儲存下面這些類型的對象:
    1.一個aType的實例
    2.它的子類的實例(如果aType是個類)
    3.實現aType接口的類實例(如果aType是個接口)

    但是,請注意,一個泛型本身是個JAVA類型,就像java.lang.String或java.io.File一樣。傳遞不同的type variable給泛型可以創建不同的JAVA類型。比如,下面例子中list1和list2引用了不同的類型對象。

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


    list1指向了一個type variables為java.lang.Objects 的List而list2指向了一個type variables為String 的List。所以傳遞一個List<String>給一個參數為List<Object>的函數將導致compile time錯誤。下面列表可以說明:

    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>();
        // 這里將產生一個錯誤
        doIt(myList);
      }
    }

    上面的代碼無法編譯,因為你試圖傳遞一個錯誤的類型給函數doIt。doIt的參數是List<Object>二你傳遞的參數是List<String>。

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

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

    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);
      }
    }
    這些代碼說明了在printList函數中,List<?>表示各種類型的List對象。然而,請注意,在聲明的時候使用 ? 通配符是不合法的,像這樣:

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

    如果你想創建一個接收任何類型對象的List,你可以使用Object作為type variable,就像這樣:

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


    在函數中使用界限通配符

    在之前的章節中,你學會了通過傳遞不同的type variables來創建不同JAVA類型的泛型,但并不考慮type variables之間的繼承關系。在很多情況下,你想一個函數有不同的List參數。比如,你有一個函數getAverage,他返回了一個List中成員的平均值。然而,如果你把List<Number>作為etAverage的參數,你就沒法傳遞List<Integer> 或List<Double>參數,因為List<Number>和List<Integer> 和List<Double>不是同樣的類型。你能使用raw type 或使用通配符,但這樣無法在compile time進行安全類型檢查,因為你能傳遞任何任何類型的List,比如List<String>的實例。你可以使用List<Number>作為參數,但是你就只能傳遞List<Number>給函數。但這樣就使你的函數功能減少,因為你可能更多的時候要操作List<Integer>或List<Long>,而不是List<Number>。

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

    使用上界規則的語法這么定義的:GenericType<? extends upperBoundType>. 比如,對getAverage函數的參數,你可以這么寫List<? extends Number>. 下面例子說明了如何使用這種規則。

    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
      }
    }
    由于有了上界規則,上面例子中的getAverage函數允許你傳遞一個List<Number> 或一個type variable是任何java.lang.Number子類的List。


    下界規則

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


    創建泛型類


    前面的章節主要說明了如何使使用泛型類,特別是Collections framework中的類。現在我們開始學習如何寫自己的泛型類。

    基本上,除了聲明一些你想要使用的type variables外,一個泛型類和別的類沒有什么區別。這些type variables位于類型后面的<>中。比如,下面的Point就是個泛型類。一個Point對象代表了一個系統中的點,它有橫坐標和縱坐標。通過使Point泛型化,你能定義一個點實例的精確程度。比如,一個Point對象需要非常精確,你能把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;
      }
    }

    在這個例子中,T是Point的type variable 。T是getX和getY的返回值類型,也是setX和setY的參數類型。此外,構造函數結合兩個T參數。

    使用point類就像使用別的類一樣。比如,下面的例子創建了兩個Point對象: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);

    總結

    泛型使代碼在compile time有了更嚴格的類型檢查。特別是在Collections framework中,泛型有兩個作用。第一,他們增加了對收集類型(collection types)在compile time的類型檢查,所以收集類所能持有的類型對傳遞給它的參數類型起了限制作用。比如你創建了一個持有strings的java.util.List實例,那么他就將不能接受Integers或別的類型。其次,當你從一個收集中取得一個元素時,泛型消除了類型轉換的必要。

    泛型能夠在沒有type variable的情況下使用,比如,作為raw types。這些措施讓Java 5之前的代碼能夠運行在JRE 5中。但是,對新的應用程序,你最好不要使用raw types,因為以后Java可能不支持他們。


    你已經知道通過傳遞不同類型的type variable給泛型類可以產生不同的JAVA類型。就是說List<String>和List<Object>的類型是不同的。盡管String是java.lang.Object。但是傳遞一個List<String>給一個參數是List<Object>的函數會參數會產生編譯錯誤(compile error)。函數能用 ? 通配符使其接受任何類型的參數。List<?> 意味著任何類型的對象。

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

    posted on 2005-08-17 21:56 R.Zeus 閱讀(406) 評論(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);
    }
    }
    }
      回復  更多評論
      
    主站蜘蛛池模板: 中文字幕无码播放免费| 久久久国产精品亚洲一区| 青青青国产在线观看免费网站| 国产天堂亚洲精品| 97久久精品亚洲中文字幕无码| 四虎影视精品永久免费| 免费人成视频在线| **一级毛片免费完整视| 最近免费中文字幕MV在线视频3| 久久亚洲精品高潮综合色a片| 亚洲欧洲日产国码二区首页| 狠狠热精品免费观看| 亚洲伊人久久大香线蕉在观| 亚洲成AV人片在线观看WWW| 亚洲精品美女久久久久99小说| 日韩午夜免费视频| 最近的中文字幕大全免费版| 最近免费中文字幕大全免费版视频| 亚洲国产模特在线播放| 亚洲va中文字幕无码久久不卡| 久久久久亚洲精品男人的天堂| 亚洲不卡AV影片在线播放| 免费永久在线观看黄网站| 日韩免费观看的一级毛片| 成人免费无遮挡无码黄漫视频| 无人在线观看完整免费版视频| 亚洲综合免费视频| 亚洲视频免费观看| 91免费在线播放| 国产精品怡红院永久免费| 韩国免费一级成人毛片| 成人毛片18岁女人毛片免费看| 成人免费视频软件网站| 免费观看美女裸体网站| 国产精品99久久免费| 免费国产在线观看| 亚洲视频在线免费| 亚洲男人的天堂www| 久久久久久久久亚洲| 亚洲福利一区二区精品秒拍| 亚洲国产日韩女人aaaaaa毛片在线|