關於Java泛型深刻理解小總結

一、何爲泛型java

首先泛型的本質即是類型參數化,通俗的說就是用一個變量來表示類型,這個類型能夠是String,Integer等等不肯定,代表可接受的類型,原理相似以下代碼程序員

int pattern; //聲明一個變量未賦值,pattern能夠看做是泛型
pattern = 4;
pattern = 5;//4和5就能夠看做是String和Integer

泛型的具體形式見泛型類、泛型方法編程

  *泛型類形式以下數組

class Test<T>
{
    private T t;
    Test(T t)
    {
        this.t = t;
    }
    public  T getT()
    {
        return t;
    }

    public void setT(T t)
    {
        this.t = t;
    }
}

  *泛型方法舉例代碼以下安全

public <T> void show()
{
    operation about T...
}

泛型參數類型聲明必須在返回類型以前this

二、爲什麼要引入泛型,即泛型與Object的優點翻譯

因爲泛型能夠接受多個參數,而Object通過強制類型轉換能夠轉換爲任何類型,既然兩者都具備相同的做用,爲什麼還要引進泛型呢?對象

解答:泛型能夠把使用Object的錯誤提早到編譯後,而不是運行後,提高安全性。如下用帶泛型的ArrayList和不帶泛型的Arraylist舉例說明繼承

代碼1:接口

ArrayList al = new ArrayList();
al.add("hello");
al.add(4);//自動裝箱
String s1 = (String)al.get(0);
String s2 = (String)al.get(1);//在編譯時沒問題,但在運行時出現問題

首先聲明無泛型的ArrayList時,其默認的原始類型是Object數組,既然爲Object類型,就能夠接受任意數據的賦值,所以編譯時沒有問題,可是在運行時,Integer強轉成String,確定會出現ClassCastException.所以泛型的引入加強了安全性,把類轉換異常提早到了編譯時期。

三、類型擦除和原始類型

  *類型擦除的由來

在JAVA的虛擬機中並不存在泛型,泛型只是爲了完善java體系,增長程序員編程的便捷性以及安全性而建立的一種機制,在JAVA虛擬機中對應泛型的都是肯定的類型,在編寫泛型代碼後,java虛擬中會把這些泛型參數類型都擦除,用相應的肯定類型來代替,代替的這一動做叫作類型擦除,而用於替代的類型稱爲原始類型,在類型擦除過程當中,通常使用第一個限定的類型來替換,若無限定則使用Object.

  *對泛型類的翻譯

泛型類(不帶泛型限定)代碼:

class Test<T>
{
    private T t;
    public void show(T t)
    {

    }
}

虛擬機進行翻譯後的原始類型:

class Test
{
    private Object t;
    public void show(Object t)
    {
        
    }
}

泛型類(帶泛型限定)代碼: 

class Test<? extends Comparable>
{
    private T t;
    public void show(T t)
    {

    }
}

虛擬機進行翻譯後的原始類型:

class Test
{
    private Comparable t;
    public void show(Comparable t)
    {
        
    }
}

 *泛型方法的翻譯

class Test<T>
{
    private T t;
    public void show(T t)
    {

    }
}

class TestDemo extends Test<String>
{
    private String t;
    public void show(String t)
    {
        
    }
}

因爲TestDemo繼承Test<String>,可是Test在類型擦除後還有一個public void Show(Object t),這和那個show(String t)出現重載,可是本意倒是沒有show(Object t)的,

所以在虛擬機翻譯泛型方法中,引入了橋方法,及在類型擦除後的show(Object t)中調用另外一個方法,代碼以下:

public void show(Object t)
{
    show((String) t);
}

四、泛型限定

  *泛型限定是經過?(通配符)來實現的,表示能夠接受任意類型,那必定有人有疑問,那?和T(兩者單獨使用時)有啥區別了,其實區別也不是很大,僅僅在對參數類型的使用上。

例如:

public void print(ArrayList<?> al)
{
    Iterator<?> it = al.iterator();
    while(it.hasNext())
    {
        System.out.println(in.next());
    }
}

public <T> void print(ArrayList<T> al)
{
    Iterator<T> it = al.iterator();
    while(it.hasNext())
    {
        T t = it.next();  //區別就在此處,T能夠做爲類型來使用,而?僅能做爲接收任意類型
        System.out.println(t);
    }
}

  *? extends SomeClass  這種限定,說明的是隻能接收SomeClass及其子類類型,所謂的「上限」

  *? super SomeClass 這種限定,說明只能接收SomeClass及其父類類型,所謂的「下限」

一下舉例? extends SomeClass說明一下這類限定的一種應用方式

因爲泛型參數類型能夠表示任意類型的類類型,若T要引用compareTo方法,如何保證在T類中定義了compareTo方法呢?利用以下代碼:

public <T extends Comparable> shwo(T a, T b)
{
    int num = a.compareTo(b);
}

此處用於限定T類型繼承自Comparable,由於T類型能夠調用compareTo方法。

  *能夠有多個類型限定,例如:

<T extends Comparable & Serializable>

這種書寫方式爲什麼把comparable寫在前邊?由於因爲類型擦除的問題,原始類型是由Comparable替換的,所以寫在前邊的是類中存在此類型的泛型方法放在前邊,避免調用方法時類型的強制轉換,提升效率。

class Test<T extends Comparable & Serializable>
{
    private T lower;
    private T upper;

    public Test(T first, T second) //此處是利用Comparable的方法,所以把Comparable寫在前邊,類型擦除後爲Comparable,若爲Serializable,還得用強制類型轉換,不然不能使用compareTo方法。
    {
        int a = first.compareTo(second);
        ...
    }
}

  *關於泛型類型限定的「繼承」誤區

總有些人誤把類型的限定看成繼承,好比:

//類型是這樣的
<Student extends Person>
//而後出現此類錯誤
ArrayList<Person> al = new ArrayList<Student>();

此處的<Person>, <Student>做爲泛型的意思是ArrayList容器的接收類型,用一個簡單的例子來講明

ArrayList是一個大型養殖場,<Person>代表的是他可以接收動物,而上邊的new語句生成的是一個只可以接收豬的養殖場(ArrayList<Student>),即把一個大型養殖場建形成了一個養豬場,如果繼承的大型養殖場確定是還能接受狗、羊....的,可是如今建形成了養豬場,那還能接受別的動物麼?因此這確定是錯誤的用法!簡而言之,泛型new時兩邊的類型參數必須一致。

五、泛型的一些基本規則約束

  *泛型的類型參數必須爲類的引用,不能用基本類型(int, short, long, byte, float, double, char, boolean)

  *泛型是類型的參數化,在使用時能夠用做不一樣類型(此處在說泛型類時會詳細說明)

  *泛型的類型參數能夠有多個,代碼舉例以下:

public <T, E> void show()
{    
    coding operation.....    
}

  *泛型可使用extends, super, ?(通配符)來對類型參數進行限定

  *因爲類型擦除,運行時類型查詢只適用於原始類型,好比instanceof、getClass()、強制類型轉換,a instanceof (Pair<Employe>),在類型擦除後即是 a instanceof Pair,所以以上運行的一些操做在虛擬機中操做都是對原始類型進行操做,不管寫的多麼虛幻,都逃不出類型擦除,由於在虛擬機種並不存在泛型。

  *不能建立參數化類型的數組

例如寫以下代碼:

Pair<String>[] table = new Pair<String>[10]; //ERROR
//讓Object[] t指向table
Object[] t = table;
//向t中添加對象
t[0] = new Pair<Employe>();
//關鍵錯誤之處
String s = table[0];

因爲Object能夠接收任何類型,在裏邊存入 new Pari<Employe>時,沒有任何問題,可是當取出的時候會出現ClassCastException,所以不能建立參數化類型數組。

  *不能實例化類型變量,及不能出現如下的相似代碼

T t = new T();
//或
T.Class

由於在類型擦除後,即是Object t = new Object();與用意不符合,即本意確定不是要調用Object.

  *不能再靜態域或方法中出現參數類型

例如以下錯誤代碼

class Test<T>
{
    private static T example;  //error
    public static void showExample() //error
    {
        action about T...
    }
    public static T showExample() //error
    {
        action about T....    
     }
}

首先方法是一個返回類型爲T的普通方法,而非泛型方法,這和在靜態方法中使用非靜態參數是同樣的,靜態方法是先於對象而存在內存中的,所以在編譯的時候,T的類型沒法肯定,必定會出現「Cannot make a static reference to a non_static reference」這樣相似的錯誤。

可是這樣的代碼就是正確的

class Test<T>
{public static <T> T show()
    {
        action
    }
}

由於此處的靜態方法是泛型方法,可使用.

  *不能拋出或捕獲泛型類的實例

    +不能拋出不能捕獲泛型類對象

    +泛型類不能擴展Throwable,注意是類不能繼承Throwable,類型參數的限定仍是能夠的。

    +catch子句不能使用類型變量,以下代碼

try
{
    ....
}
catch(T e) //error
{
    ...
}

  *類型擦除後的衝突注意

例如:

class Pair<T>
{
    public boolean equals(T value) //error
    {
        ....
    }
}

此處的錯誤的緣由不能存在同一個方法,在類型擦除後,Pair的方法爲,public boolean equals(Object value),這與從Object.class中繼承下來的equals(Object obj)衝突。

  *一個類不能成爲兩個接口類型的子類,而這兩個接口是同一接口的不一樣參數化。

例如:

class Calendar implements coparable<Calendar>{}

class GregorianCalendar extends Calendar implements Comparable<GregorianCalendar>{} //error

當類型擦除後,Calendar實現的是Comparable,而GregorianCalendar繼承了Calendar,又去實現Comparable,必然出錯!

——————————————————————————————————————————————————————————————————————

相關文章
相關標籤/搜索