16.泛型介紹及泛型類、泛型方法、泛型接口

什么是泛型

泛型是Java SE 1.5的新特性,泛型的本質(zhì)是參數(shù)化類型,也就是說所操作的數(shù)據(jù)類型被指定為一個參數(shù)。這種參數(shù)類型可以用在類、接口和方法的創(chuàng)建中,分別稱為泛型類、泛型接口、泛型方法。 Java語言引入泛型的好處是安全簡單。

泛型有什么好處

在Java SE 1.5之前,沒有泛型的情況的下,通過對類型Object的引用來實(shí)現(xiàn)參數(shù)的“任意化”,“任意化”帶來的缺點(diǎn)是要做顯式的強(qiáng)制類型轉(zhuǎn)換,而這種轉(zhuǎn)換是要求開發(fā)者對實(shí)際參數(shù)類型可以預(yù)知的情況下進(jìn)行的。對于強(qiáng)制類型轉(zhuǎn)換錯誤的情況,編譯器可能不提示錯誤,在運(yùn)行的時候才出現(xiàn)異常,這是一個安全隱患。
泛型的好處是在編譯的時候檢查類型安全,并且所有的強(qiáng)制轉(zhuǎn)換都是自動和隱式的,提高代碼的重用率。
1、泛型的類型參數(shù)只能是類類型(包括自定義類),不能是簡單類型。
2、同一種泛型可以對應(yīng)多個版本(因為參數(shù)類型是不確定的),不同版本的泛型類實(shí)例是不兼容的。
3、泛型的類型參數(shù)可以有多個。
4、泛型的參數(shù)類型可以使用extends語句,例如<T extends superclass>。習(xí)慣上稱為“有界類型”。
5、泛型的參數(shù)類型還可以是通配符類型。例如Class<?> classType = Class.forName("java.lang.String");

沒有泛型的時候是這樣的

ArrayList al = new ArrayList();
al.add("ysjian001");
al.add(1);
al.add(new Object());

這段代碼看似功能強(qiáng)大,為什么呢?因為它似乎能夠往集合添加各種類型的對象(int類型會被裝箱成Integer對象類型),貌似一些老程序員也傾向于這么去做,而且他們可以理直氣壯的告訴我理由:我這么做想存什么就存什么!先不否定這種說法,讓我們繼續(xù),看看下面代碼:

String first = (String) al.get(0);

往集合里面存值就是為了后期取出來用的,而不是System.out.println(first),這里就產(chǎn)生了一個強(qiáng)制轉(zhuǎn)換的問題,而往往這種類型的強(qiáng)制轉(zhuǎn)換在編譯器是允許通過的,而寫程序的人們會犯下無意間的錯誤,錯誤的進(jìn)行了強(qiáng)制轉(zhuǎn)換,導(dǎo)致程序運(yùn)行失敗。

強(qiáng)制類型轉(zhuǎn)換導(dǎo)致的程序運(yùn)行失敗的原因是沒有在編譯器對類型進(jìn)行控制,看看code01調(diào)用ArrayList對象的add方法,任何類型都是可以添加進(jìn)行的,編譯器無法進(jìn)行錯誤檢驗,埋下了安全隱患,例如:

ArrayList al = new ArrayList();
// 無法進(jìn)行錯誤檢查,F(xiàn)ile對象可以添加進(jìn)去,編譯器和運(yùn)行期都可以通過
al.add(new File()); 
String first = (String) al.get(0);  // 類型轉(zhuǎn)換失敗導(dǎo)致運(yùn)行失敗

不用泛型的缺點(diǎn)

沒有泛型的程序面臨兩個問題:

  1. 編譯器無法進(jìn)行類型檢查,可以向集合中添加任意類型的對象。
  2. 取值時類型轉(zhuǎn)換失敗導(dǎo)致程序運(yùn)行失敗。

沒有泛型的程序?qū)е碌暮蠊?/p>

  1. 程序的可讀性有所降低,因為程序員可以不受限制往集合中添加任意對象。
  2. 程序的安全性遭到質(zhì)疑,類型轉(zhuǎn)換失敗將導(dǎo)致程序運(yùn)行失敗。

Java5泛型提供了一個更好的解決方案:類型參數(shù)(type parameters),使用泛型的程序改善上述代碼如下:

ArrayList<String> al = new ArrayList<String>();
al.add( "ysjian001");
// al.add(new Thread()); // 定義了String類型參數(shù),添加File對象會報錯
String first =  al.get(0);// 使用泛型后取值不用進(jìn)行類型轉(zhuǎn)換

到這里,通過前后對比,泛型的好處是不是很清楚了呢?為什么用泛型呢?
因為出現(xiàn)編譯錯誤比類在運(yùn)行時出現(xiàn)強(qiáng)制類型轉(zhuǎn)換異常要好得多,泛型的好處在于提高了程序的可讀性和安全性,這也是程序設(shè)計的宗旨之一。

什么時候使用泛型

使用泛型類是一件很輕松的事,集合框架中的類都是泛型類,用起來很方便。有人會想類型限制我們?yōu)槭裁床恢苯佑脭?shù)組呢?這個問題就好像問為什么集合優(yōu)于數(shù)組,數(shù)組是固定的,而集合是可以自動擴(kuò)展的。另外在實(shí)際中,實(shí)現(xiàn)一個泛型其實(shí)并不是那么容易。

大多數(shù)應(yīng)用程序員對泛型的熟練程度僅僅停留在使用泛型上,像集合類中的List、Set和Map這些泛型集合用的很多,他們不必考慮這些泛型集合的工作方式和原理。那么當(dāng)把不同的泛型類混合在一起使用時,或者對Java5之前的遺留代碼進(jìn)行銜接時,可能會看到含糊不清的的錯誤消息。這樣一來,程序員就需要學(xué)習(xí)Java泛型來解決問題了,而不是在程序中胡亂猜測了。最終,部分程序員可能想要實(shí)現(xiàn)自己的泛型類和泛型方法。

提煉出泛型程序設(shè)計的三種熟練程度就是:

  1. 僅僅使用泛型。
  2. 學(xué)習(xí)泛型解決一些問題。
  3. 掌握泛型,實(shí)現(xiàn)自己的泛型。

怎么使用泛型

如何使用泛型聽起來是一件很容易的事情,因為Sun公司的那些工程師已經(jīng)做了很大努力,而需求總是會稍微苛刻一點(diǎn)的,需要解決因為缺乏類型參數(shù)模糊不清的問題,或者我們有必要實(shí)現(xiàn)自己的泛型來滿足業(yè)務(wù)需求,所以學(xué)習(xí)和掌握泛型是很有必要的。最常見的自定義泛型的場景是泛型類、泛型方法、泛型接口。

泛型類

把泛型定義在類上
格式:public class 類名<泛型類型1,…>
注意:泛型類型必須是引用類型

早期的時候,我們使用Object來代表任意的類型。
向上轉(zhuǎn)型是沒有任何問題的,但是在向下轉(zhuǎn)型的時候其實(shí)隱含了類型轉(zhuǎn)換的問題。
也就是說這樣的程序其實(shí)并不是安全的。所以Java在JDK5后引入了泛型,提高程序的安全性。
下面我們就來學(xué)習(xí)泛型類是怎么回事

// 泛型類:把泛型定義在類上 
public class ObjectTool<T> { 
      private T obj; 
      public T getObj() { 
         return obj; 
      } 
      public void setObj(T obj) { 
           this.obj = obj;
     }
}
//  泛型類的測試 
public class ObjectToolDemo { 
    public static void main(String[] args) { 
        ObjectTool<String> ot = new ObjectTool<String>();    
        ot.setObj(new String("中國")); 
        String s = ot.getObj(); 
        System.out.println("姓名是:" + s); 
        ObjectTool<Integer> ot2 = new ObjectTool<Integer>();    
        ot2.setObj(new Integer(69)); 
        Integer i = ot2.getObj(); 
        System.out.println("年齡是:" + i); 
   }
}

輸出結(jié)果:
姓名是:中國
年齡是:69

泛型方法

把泛型定義在方法上
格式:public <泛型類型> 返回類型 方法名(泛型類型 .)

上面我們把泛型定義在了類中,現(xiàn)在我們也可以把泛型定義在方法中,來一起學(xué)習(xí)

* * 泛型方法:把泛型定義在方法上 **
public class ObjectTool {  
      public <T> void show(T t) {
           System.out.println(t); 
      }
}
  public class ObjectToolDemo { 
        public static void main(String[] args) { 
             // 定義泛型方法后
            ObjectTool ot = new ObjectTool(); 
            ot.show("hello"); 
            ot.show(100);
            ot.show(true);
       }
  }

這樣我們就可以傳遞任意類型的參數(shù)了

泛型接口

把泛型定義在接口上
格式:public interface 接口名<泛型類型1…>

/* * 泛型接口:把泛型定義在接口上 */
public interface Inter<T> { 
  public abstract void show(T t);
}
//實(shí)現(xiàn)類在實(shí)現(xiàn)接口的時候,我們會遇到兩種情況
//第一種情況:已經(jīng)知道是什么類型的了
public class InterImpl implements Inter<String> { 
@Override 
public void show(String t) { 
  System.out.println(t);
}
}
//第二種情況:還不知道是什么類型的
public class InterImpl<T> implements Inter<T> { 
@Override 
public void show(T t) { 
       System.out.println(t);
}
}
public class InterDemo { 
  public static void main(String[] args) {
      // 第一種情況的測試
     Inter<String> i = new InterImpl(); 
     i.show("hello"); 
     // 第二種情況的測試
     Inter<String> i = new InterImpl<String>(); 
     i.show("hello"); 
     Inter<Integer> ii = new InterImpl<Integer>(); 
     ii.show(100);
}
}

我們來寫一個簡單的例子驗證一下上面所說的結(jié)論

public class GenericDemo {
    public static void main(String[] args) {
        // 泛型如果明確的寫的時候,前后必須一致 Collection<Object> c1 = new ArrayList<Object>();
        // Collection<Object> c2 = new ArrayList<Animal>();//報錯
        // Collection<Object> c3 = new ArrayList<Dog>();//報錯
        // Collection<Object> c4 = new ArrayList<Cat>();//報錯

        // ?表示任意的類型都是可以的
        Collection<?> c5 = new ArrayList<Object>();
        Collection<?> c6 = new ArrayList<Animal>();
        Collection<?> c7 = new ArrayList<Dog>();
        Collection<?> c8 = new ArrayList<Cat>();

        // ? extends E:向下限定,E及其子類
        // Collection<? extends Animal> c9 = new ArrayList<Object>();//報錯
        Collection<? extends Animal> c10 = new ArrayList<Animal>();
        Collection<? extends Animal> c11 = new ArrayList<Dog>();
        Collection<? extends Animal> c12 = new ArrayList<Cat>();

        // ? super E:向上限定,E極其父類
        Collection<? super Animal> c13 = new ArrayList<Object>();
        Collection<? super Animal> c14 = new ArrayList<Animal>();
        // Collection<? super Animal> c15 = new ArrayList<Dog>();//報錯
        // Collection<? super Animal> c16 = new ArrayList<Cat>();//報錯
    }
}

class Animal {
}

class Dog extends Animal {
}

class Cat extends Animal {
}

仔細(xì)觀察一下上面的通配符有什么區(qū)別,你會很快的學(xué)會通配符的使用

泛型在實(shí)際開發(fā)中的應(yīng)用場景

泛型在實(shí)際開發(fā)中用的很多,比如常見的Dao層的父類BaseDao,這里面一般是一個泛型類,把實(shí)體作為通配符傳過去。

源碼下載

本工程詳細(xì)源碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. 泛型概述 泛型(Generic type 或者 generics)是對 Java 語言的類型系統(tǒng)的一種擴(kuò)展,...
    JackChen1024閱讀 559評論 0 3
  • object 變量可指向任何類的實(shí)例,這讓你能夠創(chuàng)建可對任何數(shù)據(jù)類型進(jìn)程處理的類。然而,這種方法存在幾個嚴(yán)重的問題...
    CarlDonitz閱讀 1,021評論 0 5
  • 那時他們還小。 父親常年隨機(jī)械隊外出攬活,母親一下車間就是大半天,沒人管他們兄妹。他身子弱,喜文靜;小妹則相反,母...
    小陳皮閱讀 249評論 0 3
  • 有一件事,我一直很內(nèi)疚,至少是前半生。 雖然我的閨蜜這些年來一遍遍的跟我說,沒關(guān)系不是你的錯,即使你不那么做,最后...
    滄角閱讀 1,615評論 0 2
  • 大數(shù)據(jù)時代的到來,令人類的競爭變得越來越激烈,不光成年人對自己的未來憂心忡忡,連兒童(包括沒出生的)也被卷進(jìn)了競爭...
    琢愛舟閱讀 2,045評論 14 34

友情鏈接更多精彩內(nèi)容