欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
J2SE 5.0中的泛型
J2SE 5.0中的泛型

作者:Budi Kurniawan

翻譯:RR00

email:di_feng_ro@hotmail.com


版權聲明:可以任意轉載,轉載時(shí)請務(wù)必以超鏈接形式標明文章原始出處和作者信息及本聲明
英文原文地址:
http://www.onjava.com/pub/a/onjava/2005/07/06/generics.html
中文地址:
http://www.matrix.org.cn/resource/article/43/43634_java_generics.html
關(guān)鍵詞: java generics java5

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

沒(méi)有泛型的日子
     所有的java類(lèi)都源自java.lang.Object,這意味著(zhù)所有的JAVA對象能轉換成Object。因此,在之前的JDK的版本中,很多集合框架的函數接受一個(gè)Object參數。所以,collections是一個(gè)能持有任何對象的多用途工具,但帶來(lái)了不良的后果。

     舉個(gè)簡(jiǎn)單的例子,在JDK 5.0的之前版本中,類(lèi)List的函數add接受一個(gè)Object參數:

public boolean add(java.lang.Object element)


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

public java.lang.Object get(int index) throws IndexOutOfBoundsException


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

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


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

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


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


泛型入門(mén)

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

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


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

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


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

List<String> myList;


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

NOTE:根據慣例,我們使用一個(gè)唯一的大寫(xiě)字目表示一個(gè)類(lèi)型變量。為了創(chuàng )建一個(gè)泛型,你需在聲明時(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)的類(lèi)型而V用來(lái)表示值(VALUE)的類(lèi)型。put和values是這么定義的:

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


NOTE:一個(gè)泛型不準直接的或間接的是java.lang.Throwable的子類(lèi)。因為異常是在運行時(shí)拋出的,所以它不可能預言什么類(lèi)型的異常將在編譯時(shí)拋出.

列表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對象。當然,在另外的情況中,你能新建能接受各種對象的List實(shí)例。注意,當從List實(shí)例中返回成員元素時(shí),不需要對象轉化,因為他返回的了你想要的類(lèi)型,也就是String.

NOTE:泛型的類(lèi)型檢查(type checking)是在編譯時(shí)完成的.

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

List<List<String>> myListOfListsOfStrings;


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

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


下一個(gè)列表中的ListOfListsTest類(lèi)示范了一個(gè)List(命名為listOfLists)接受一個(gè)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"
  }
}


另外,一個(gè)泛型接受一個(gè)或多個(gè)類(lèi)型變量。比如,java.util.Map有兩個(gè)類(lèi)型變量s。第一個(gè)定義了鍵(key)的類(lèi)型,第二個(gè)定義了值(value)的類(lè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值,我們不需要任何類(lèi)型轉換。

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

    既然在J2SE 5.0中收集類(lèi)型已經(jīng)泛型化,那么,原來(lái)的使用這些類(lèi)型的代碼將如何呢?很幸運,他們在JAVA 5中將繼續工作,因為你能使用沒(méi)有參數的泛型。比如,你能繼續像原來(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)有任何參數的泛型被稱(chēng)為原型(raw type)。它意味著(zhù)這些為JDK1.4或更早的版本而寫(xiě)的代碼將繼續在java 5中工作。

盡管如此,一個(gè)需要注意的事情是,JDK5編譯器希望你使用帶參數的泛型。否則,編譯器將提示警告,因為他認為你可能忘了定義類(lèi)型變量s。比如,編譯上面的代碼的時(shí)候你會(huì )看到下面這些警告,因為第一個(gè)List被認為是原型。

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

當你使用原型時(shí),如果你不想看到這些警告,你有幾個(gè)選擇來(lái)達到目的:
1.編譯時(shí)帶上參數-source 1.4
2.使用@SupressWarnings("unchecked")注釋
3.更新你的代碼,使用List<Object>. List<Object>的實(shí)例能接受任何類(lèi)型的對象,就像是一個(gè)原型List。然而,編譯器不會(huì )報錯。

使用 ? 通配符
   前面提過(guò),如果你聲明了一個(gè)List<aType>, 那么這個(gè)List對aType起作用,所以你能儲存下面這些類(lèi)型的對象:
1.一個(gè)aType的實(shí)例
2.它的子類(lèi)的實(shí)例(如果aType是個(gè)類(lèi))
3.實(shí)現aType接口的類(lèi)實(shí)例(如果aType是個(gè)接口)
但是,請注意,一個(gè)泛型本身是個(gè)JAVA類(lèi)型,就像java.lang.String或java.io.File一樣。傳遞不同的類(lèi)型變量給泛型可以創(chuàng )建不同的JAVA類(lèi)型。比如,下面例子中list1和list2引用了不同的類(lèi)型對象。

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


list1指向了一個(gè)類(lèi)型變量s為java.lang.Objects 的List而list2指向了一個(gè)類(lèi)型變量s為String 的List。所以傳遞一個(gè)List<String>給一個(gè)參數為L(cháng)ist<Object>的函數將導致compile time錯誤。下面列表可以說(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è)錯誤
    doIt(myList);
  }
}

上面的代碼無(wú)法編譯,因為你試圖傳遞一個(gè)錯誤的類(lèi)型給函數doIt。doIt的參數是List<Object>二你傳遞的參數是List<String>。
可以使用 ? 通配符解決這個(gè)難題。List<?> 意味著(zhù)一個(gè)對任何對象起作用的List。所以,doIt可以改為:

public static void doIt(List<?> l) {}


    在某些情況下你會(huì )考慮使用 ? 通配符。比如,你有一個(gè)printList函數,這個(gè)函數打印一個(gè)List的所有成員,你想讓這個(gè)函數對任何類(lèi)型的List起作用時(shí)。否則,你只能累死累活的寫(xiě)很多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);
  }
}


這些代碼說(shuō)明了在printList函數中,List<?>表示各種類(lèi)型的List對象。然而,請注意,在聲明的時(shí)候使用 ? 通配符是不合法的,像這樣:

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


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

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


在函數中使用界限通配符
在之前的章節中,你學(xué)會(huì )了通過(guò)傳遞不同的類(lèi)型變量s來(lái)創(chuàng )建不同JAVA類(lèi)型的泛型,但并不考慮類(lèi)型變量s之間的繼承關(guān)系。在很多情況下,你想一個(gè)函數有不同的List參數。比如,你有一個(gè)函數getAverage,他返回了一個(gè)List中成員的平均值。然而,如果你把List<Number>作為getAverage的參數,你就沒(méi)法傳遞List<Integer> 或List<Double>參數,因為L(cháng)ist<Number>和List<Integer> 和List<Double>不是同樣的類(lèi)型。

你能使用原型或使用通配符,但這樣無(wú)法在編譯時(shí)進(jìn)行安全類(lèi)型檢查,因為你能傳遞任何類(lèi)型的List,比如List<String>的實(shí)例。你可以使用List<Number>作為參數,但是你就只能傳遞List<Number>給函數。但這樣就使你的函數功能減少,因為你可能更多的時(shí)候要操作List<Integer>或List<Long>,而不是List<Number>。

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

使用上界規則的語(yǔ)法這么定義的:GenericType<? extends upperBoundType>. 比如,對getAverage函數的參數,你可以這么寫(xiě)List<? extends Number>. 下面例子說(shuō)明了如何使用這種規則。
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函數允許你傳遞一個(gè)List<Number> 或一個(gè)類(lèi)型變量是任何java.lang.Number子類(lèi)的List。

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

創(chuàng )建泛型

前面的章節主要說(shuō)明了如何使使用泛型,特別是集合框架中的類(lèi)?,F在我們開(kāi)始學(xué)習如何寫(xiě)自己的泛型。

基本上,除了聲明一些你想要使用的類(lèi)型變量s外,一個(gè)泛型和別的類(lèi)沒(méi)有什么區別。這些類(lèi)型變量s位于類(lèi)型后面的<>中。比如,下面的Point就是個(gè)泛型。一個(gè)Point對象代表了一個(gè)系統中的點(diǎn),它有橫坐標和縱坐標。通過(guò)使Point泛型化,你能定義一個(gè)點(diǎn)實(shí)例的精確程度。比如,如果一個(gè)Point對象需要非常精確,你就把Double作為類(lèi)型變量。否則,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的類(lèi)型變量 。T是getX和getY的返回值類(lèi)型,也是setX和setY的參數類(lèi)型。此外,構造函數結合兩個(gè)T參數。
使用point類(lèi)就像使用別的類(lèi)一樣。比如,下面的例子創(chuàng )建了兩個(gè)Point對象:ponint1和point2。前者把Integer作為類(lèi)型變量,而后者把Double作為類(lèi)型變量。

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);


總結
泛型使代碼在編譯時(shí)有了更嚴格的類(lèi)型檢查。特別是在集合框架中,泛型有兩個(gè)作用。第一,他們增加了對集合類(lèi)型在編譯時(shí)的類(lèi)型檢查,所以集合類(lèi)所能持有的類(lèi)型對傳遞給它的參數類(lèi)型起了限制作用。比如你創(chuàng )建了一個(gè)持有strings的java.util.List實(shí)例,那么他就將不能接受Integers或別的類(lèi)型。其次,當你從一個(gè)集合中取得一個(gè)元素時(shí),泛型消除了類(lèi)型轉換的必要。
泛型能夠在沒(méi)有類(lèi)型變量的情況下使用,比如,作為原型。這些措施讓Java 5之前的代碼能夠運行在JRE 5中。但是,對新的應用程序,你最好不要使用原型,因為以后Java可能不支持他們。

你已經(jīng)知道通過(guò)傳遞不同類(lèi)型的類(lèi)型變量給泛型可以產(chǎn)生不同的JAVA類(lèi)型。就是說(shuō)List<String>和List<Object>的類(lèi)型是不同的。盡管String是java.lang.Object。但是傳遞一個(gè)List<String>給一個(gè)參數是List<Object>的函數會(huì )參數會(huì )產(chǎn)生編譯錯誤(compile error)。函數能用 ? 通配符使其接受任何類(lèi)型的參數。List<?> 意味著(zhù)任何類(lèi)型的對象。
最后,你已經(jīng)看到了寫(xiě)一個(gè)泛型和別的一般JAVA類(lèi)沒(méi)有什么區別。你只需要在類(lèi)型名稱(chēng)后面的<>中聲明一系列的類(lèi)型變量s就行了。這些類(lèi)型變量s就是返回值類(lèi)型或者參數類(lèi)型。根據慣例,一個(gè)類(lèi)型變量用一個(gè)大寫(xiě)字母表示。


Budi Kurniawan是一個(gè)高級 J2EE 架構師和作家。
本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
太驚悚了!上班摸魚(yú)一萬(wàn)字,就是為了讓你理解Java泛型
vb.net2005中什么類(lèi)型的變量與Delphi的StringList是相似的
深入分析Java的序列化與反序列化
Java中的泛型 第一部分
java通配符
Java泛型簡(jiǎn)明教程 | 外刊IT評論
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久