天天看點

泛型

目錄

<dl></dl>

<dd></dd>

展開

  1、泛型的類型參數隻能是類類型(包括自定義類),不能是簡單類型。

  3、泛型的類型參數可以有多個。

  4、泛型的參數類型可以使用extends語句,例如&lt;T extends superclass&gt;。習慣上稱為“有界類型”。

  5、泛型的參數類型還可以是通配符類型。例如Class&lt;?&gt; classType = Class.forName("java.lang.String");

  泛型還有接口、方法等等,内容很多,需要花費一番功夫才能了解掌握并熟練應用。在此給出我曾經了解泛型時候寫出的兩個例子(根據看的印象寫的),實作同樣的功能,一個使用了泛型,一個沒有使用,通過對比,可以很快學會泛型的應用,學會這個基本上學會了泛型70%的内容。

  例子一:使用了泛型

  class Gen&lt;T&gt; {

  private T ob; //定義泛型成員變量

  public Gen(T ob) {

  this.ob = ob;

  }

  public T getOb() {

  return ob;

  public void setOb(T ob) {

  public void showType() {

  System.out.println("T的實際類型是: " + ob.getClass().getName());

  public class GenDemo {

  public static void main(String[] args){

  //定義泛型類Gen的一個Integer版本

  Gen&lt;Integer&gt; intOb=new Gen&lt;Integer&gt;(88);

  intOb.showType();

  int i= intOb.getOb();

  System.out.println("value= " + i);

  System.out.println("----------------------------------");

  //定義泛型類Gen的一個String版本

  Gen&lt;String&gt; strOb=new Gen&lt;String&gt;("Hello Gen!");

  strOb.showType();

  String s=strOb.getOb();

  System.out.println("value= " + s);

  例子二:沒有使用泛型

  public class Gen2 {

  private Object ob; //定義一個通用類型成員

  public Gen2(Object ob) {

  public Object getOb() {

  public void setOb(Object ob) {

  public void showTyep() {

  public class GenDemo2 {

  public static void main(String[] args) {

  //定義類Gen2的一個Integer版本

  Gen2 intOb = new Gen2(new Integer(88));

  intOb.showTyep();

  int i = (Integer) intOb.getOb();

  System.out.println("---------------------------------");

  //定義類Gen2的一個String版本

  Gen2 strOb = new Gen2("Hello Gen!");

  strOb.showTyep();

  String s = (String) strOb.getOb();

  運作結果:

  兩個例子運作Demo結果是相同的,控制台輸出結果如下:

  T的實際類型是:

  java.lang.Integer

  value= 88

  ----------------------------------

  T的實際類型是: java.lang.String

  value= Hello Gen!

  Process finished with exit code 0

  看明白這個,以後基本的泛型應用和代碼閱讀就不成問題了。

  有兩個類如下,要構造兩個類的對象,并列印出各自的成員x。

  public class StringFoo {

  private String x;

  public StringFoo(String x) {

  this.x = x;

  public String getX() {

  return x;

  public void setX(String x) {

  public class DoubleFoo {

  private Double x;

  public DoubleFoo(Double x) {

  public Double getX() {

  public void setX(Double x) {

  以上的代碼實在無聊,就不寫如何實作了。

  因為上面的類中,成員和方法的邏輯都一樣,就是類型不一樣,是以考慮重構。Object是所有類的父類,是以可以考慮用Object做為成員類型,這樣就可以實作通用了,實際上就是“Object泛型”,暫時這麼稱呼。

  public class ObjectFoo {

  private Object x;

  public ObjectFoo(Object x) {

  public Object getX() {

  public void setX(Object x) {

  寫出Demo方法如下:

  public class ObjectFooDemo {

  public static void main(String args[]) {

  ObjectFoo strFoo = new ObjectFoo(new StringFoo("Hello Generics!"));

  ObjectFoo douFoo = new ObjectFoo(new DoubleFoo(Double("33")));

  ObjectFoo objFoo = new ObjectFoo(new Object());

  System.out.println("strFoo.getX="+(StringFoo)strFoo.getX());

  System.out.println("douFoo.getX="+(DoubleFoo)douFoo.getX());

  System.out.println("objFoo.getX="+objFoo.getX());

  運作結果如下:

  strFoo.getX=Hello Generics!

  douFoo.getX=33.0

  objFoo.getX=java.lang.Object@19821f

  解說:在Java 5之前,為了讓類有通用性,往往将參數類型、傳回類型設定為Object類型,當擷取這些傳回類型來使用時候,必須将其“強制”轉換為原有的類型或者接口,然後才可以調用對象上的方法。

  強制類型轉換很麻煩,我還要事先知道各個Object具體類型是什麼,才能做出正确轉換。否則,要是轉換的類型不對,比如将“Hello Generics!”字元串強制轉換為Double,那麼編譯的時候不會報錯,可是運作的時候就挂了。那有沒有不強制轉換的辦法----有,改用 Java5泛型來實作。

  public class GenericsFoo&lt;T&gt; {

  private T x;

  public GenericsFoo(T x) {

  public T getX() {

  public void setX(T x) {

  public class GenericsFooDemo {

  public static void main(String args[]){

  GenericsFoo&lt;String&gt; strFoo=new GenericsFoo&lt;String&gt;("Hello Generics!");

  GenericsFoo&lt;Double&gt; douFoo=new GenericsFoo&lt;Double&gt;(new Double("33"));

  GenericsFoo&lt;Object&gt; objFoo=new GenericsFoo&lt;Object&gt;(new Object());

  System.out.println("strFoo.getX="+strFoo.getX());

  System.out.println("douFoo.getX="+douFoo.getX());

  和使用“Object泛型”方式實作結果的完全一樣,但是這個Demo簡單多了,裡面沒有強制類型轉換資訊。

  下面解釋一下上面泛型類的文法:

  使用&lt;T&gt;來聲明一個類型持有者名稱,然後就可以把T當作一個類型代表來聲明成員、參數和傳回值類型。

  當然T僅僅是個名字,這個名字可以自行定義。

  class GenericsFoo&lt;T&gt; 聲明了一個泛型類,這個T沒有任何限制,實際上相當于Object類型,實際上相當于 class GenericsFoo&lt;T extends Object&gt;。

  與Object泛型類相比,使用泛型所定義的類在聲明和構造執行個體的時候,可以使用“&lt;實際類型&gt;”來一并指定泛型類型持有者的真實類型。類如

  當然,也可以在構造對象的時候不使用尖括号指定泛型類型的真實類型,但是你在使用該對象的時候,就需要強制轉換了。比如:GenericsFoo douFoo=new GenericsFoo(new Double("33"));

  實際上,當構造對象時不指定類型資訊的時候,預設會使用Object類型,這也是要強制轉換的原因。

  在上面的例子中,由于沒有限制class GenericsFoo&lt;T&gt;類型持有者T的範圍,實際上這裡的限定類型相當于Object,這和“Object泛型”實質是一樣的。限制比如我們要限制T為集合接口類型。隻需要這麼做:

  注意:&lt;T extends Collection&gt;這裡的限定使用關鍵字 extends,後面可以是類也可以是接口。但這裡的extends已經不是繼承的含義了,應該了解為T類型是實作Collection接口的類型,或者T是繼承了XX類的類型。

  下面繼續對上面的例子改進,我隻要實作了集合接口的類型:

  public class CollectionGenFoo&lt;T extends Collection&gt; {

  public CollectionGenFoo(T x) {

  執行個體化的時候可以這麼寫:

  public class CollectionGenFooDemo {

  CollectionGenFoo&lt;ArrayList&gt; listFoo = null;

  listFoo = new CollectionGenFoo&lt;ArrayList&gt;(new ArrayList());

  //出錯了,不讓這麼幹。

  // CollectionGenFoo&lt;Collection&gt; listFoo = null;

  // listFoo=new CollectionGenFoo&lt;ArrayList&gt;(new ArrayList());

  System.out.println("執行個體化成功!");

  目前看到的這個寫法是可以編譯通過,并運作成功。可是注釋掉的兩行加上就出錯了,因為&lt;T extends Collection&gt;這麼定義類型的時候,就限定了構造此類執行個體的時候T是确定的一個類型,這個類型實作了Collection接口,但是實作 Collection接口的類很多很多,如果針對每一種都要寫出具體的子類類型,那也太麻煩了,我幹脆還不如用Object通用一下。别急,泛型針對這種情況還有更好的解決方案,那就是“通配符泛型”。

  為了解決類型被限制死了不能動态根據執行個體來确定的缺點,引入了“通配符泛型”,針對上面的例子,使用通配泛型格式為&lt;? extends Collection&gt;,“?”代表未知類型,這個類型是實作Collection接口。那麼上面實作的方式可以寫為:

  //現在不會出錯了

  CollectionGenFoo&lt;? extends Collection&gt; listFoo1 = null;

  listFoo=new CollectionGenFoo&lt;ArrayList&gt;(new ArrayList());

  注意:

  1、如果隻指定了&lt;?&gt;,而沒有extends,則預設是允許Object及其下的任何Java類了。也就是任意類。

  2、通配符泛型不單可以向下限制,如&lt;? extends Collection&gt;,還可以向上限制,如&lt;? super Double&gt;,表示類型隻能接受Double及其上層父類類型,如Number、Object類型的執行個體。

  3、泛型類定義可以有多個泛型參數,中間用逗号隔開,還可以定義泛型接口,泛型方法。這些都泛型類中泛型的使用規則類似。

  是否擁有泛型方法,與其所在的類是否泛型沒有關系。要定義泛型方法,隻需将泛型參數清單置于傳回值前。如:

  public class ExampleA {

  public &lt;T&gt; void f(T x) {

  System.out.println(x.getClass().getName());

  ExampleA ea = new ExampleA();

  ea.f(" ");

  ea.f(10);

  ea.f('a');

  ea.f(ea);

  輸出結果:

  java.lang.String

  java.lang.Character

  ExampleA

  使用泛型方法時,不必指明參數類型,編譯器會自己找出具體的類型。泛型方法除了定義不同,調用就像普通方法一樣。

  需要注意,一個static方法,無法通路泛型類的類型參數,是以,若要static方法需要使用泛型能力,必須使其成為泛型方法。

<dt>擴充閱讀:</dt>

1

<a href="http://www.java2000.net/p7926" target="_blank">http://www.java2000.net/p7926</a>

2

Java 泛型的了解與等價實作

3

<a href="http://www.java2000.net/p7927" target="_blank">http://www.java2000.net/p7927</a>

4

Java 5 泛型深入研究

<dt>開放分類:</dt>