java 泛型 竄講

1、爲何使用泛型

     複用性:泛型的本質就是參數化類型,於是使用編寫的泛型代碼能夠被許多不一樣類型的對象所複用。java

     安全性:在對類型Object引用的參數操做時,每每須要進行顯式的強制類型轉換。這種強制類型轉換須要在運行時才能被發現是否轉換異常,經過引入泛型能將在運行時才能檢查類型轉換,提早到編譯時期就能檢查。數組

2、自定義泛型

java中自定義泛型分爲三種:泛型類、泛型接口、泛型方法。安全

下面使用一個案例演示泛型類、泛型方法,泛型接口相似,因此再也不演示。app

// 自定義泛型類
public class Generic<T>
{
    private T second;
    public void setSecond(T newValue)
    {
        second = newValue;
    }
    // 自定義泛型方法
    public static <W> void printValue(W obj)
    {
        System.out.println(obj.toString());
    }
   
    @Override
    public String toString()
    {
        return second.toString();
    }
   
    public static void main(String[] args)
    {
        //
        Generic<String> g = new Generic<String>();
        g.setSecond("zhang");
        System.out.println(g);
       
        // 使用泛型方法
        Generic.printValue(45);
        Generic.printValue("hello");
    }
}

泛型方法能夠在廣泛類中定義,也能夠在泛型類中定義。

3、java泛型的特性

一、擦除

       java中的泛型是僞泛型,由於在編譯期間,全部的泛型信息都會被擦除,而只保留原始類型。好比,代碼List<Double>和List<String>在通過編譯後,都會變成List類型。
       爲何會出現這種狀況呢?這跟java的虛擬機有莫大的關係。java虛擬機沒有泛型類型對象-----全部對象都屬於普通類。因爲在java1.5以前沒有泛型,那以前沒有泛型的代碼怎麼與有泛型的代碼共存了,爲了兼容性,java虛擬機採用統一的普通類。
下面使用代碼,體現類型擦除。
public static void main(String[] args)
{
    ArrayList<String> arrayList1=new ArrayList<String>(); 
    arrayList1.add("abc"); 
    ArrayList<Double> arrayList2=new ArrayList<Double>(); 
    arrayList2.add(666.666); 
    System.out.println(arrayList1.getClass()==arrayList2.getClass()); 
}
輸出結果:

trueide

二、補償

public static void main(String[] args)
{
    ArrayList<String> arrayList1=new ArrayList<String>(); 
    arrayList1.add("abc"); 
    String str = arrayList1.get(0);    // 編譯正常
    int str2 = arrayList1.get(0);      // 編譯報錯  
}
arrayList1.get(0);
編譯事後,泛型會被擦除,arrayList1.get(0)返回Object類型。可是因爲java的補償機制,所以編譯器會自動的插入String的強制類型轉換。因爲int類型的str2不能接收通過強制轉換的String類型,於是編譯報錯。
 

4、java泛型的約束

一、不能用基本類型實例化類型參數
好比:錯誤-->Arraylist<double>; 正確-->Arraylist<Double>
二、運行時類型查詢只適用於原始類型
三、不能建立參數化類型的數組
四、不能實例化類型變量
五、泛型類的靜態上下文中類型變量無效
好比:private static T single; // ERROR
六、不能拋出或捕獲泛型類的實例

5、通配符類型


一、限定上界通配符

好比:List<? extends Animal>,表示任何泛型List類型,它的類型參數是Animal類及子類,List<Animal>、List<Cat>、List<Dog>都是List<? extends Animal>子類型。
public static void main(String[] args)
{
    List<? extends Animate> animates = new ArrayList<Animate>();         // OK
    List<? extends Animate> animates1 = new ArrayList<Cat>();            // OK
    List<Animate> animates2 = new ArrayList<Animate>();                  // OK
    List<Animate> animates3 = new ArrayList<Cat>();                      // compile-time error       
}
其實,咱們能夠將List<? extends Animate> animates 看作是List<Animate>、List<Cat>等的集合。
概括:假如給定的泛型類型爲G,兩個具體的泛型參數X、Y,當Y是X的子類時(Y extends X)
  • G<? extends Y> 是 G<? extends X>的子類型(如List<? extends Cat> 是 List<? extends Animal>的子類型)。
  • G<X> 是 G<? extends X>的子類型(如List<Animal> 是 List<? extends Animal>的子類型)
  • G<?> 與 G<? extends Object>等同,如List<?> 與List<? extends Objext>等同

學到這裏,可能會遇到一些疑惑的地方,或者說事理解不透的地方,先觀察以下兩段代碼片斷,判斷一下其是否可行??
List<? extends Animal> animal = new ArrayList<>();
animal.add(new Animal());
animal.add(new Cat());
上面的兩個add操做都不能經過編譯。爲何呢?因爲List:add(E e)加入泛型變成List<? extends Animal>:add(? extends Animal e),? extends Animal參數類型沒法肯定,能夠是Animal、Cat等,因此爲了保護其類型的一致性,所以不容許向list對象中添加任意對象,除了null。
 
注意:上界限定通常用在:? extends T get()方法上(讀數據操做)
 

二、限定下界通配符

好比:List<? super Cat>,這個通配符限制爲Cat的全部超類型(包括本類)。
它的概括方法與上界通配符類似,這裏就不囉嗦了。
看下面代碼:
List<? super Animate> animates = new ArrayList<>();    
animates.add(new Animate());
animates.add(new Cat());
上述代碼編譯經過,編譯器不知道add方法的確切類型,可是能夠用任意Animal對象(或子類型對象)。
 
注意:下界通配符一般用於:set(? extends T>)方法上(寫入數據操做)

三、無限定通配符

好比:List<?>,當類型不肯定時,才使用,該通配符較少使用。
 
關於上下界限定通配符,建議參考:《編寫高質量代碼:改善java程序的151個建議》中建議96:不一樣的場景使用不一樣的泛型通配符
 
參考

一、Java 泛型通配符?解惑

相關文章
相關標籤/搜索