Java學習點滴——泛型

基於《Java編程思想》第四版java

前言

雖然Java的泛型在語法上和C++相比是相似的,但在實現上二者是全然不一樣的。編程

語法

Java只須要一個<>就可定義泛型。在<>中可使用任意符合Java語法要求的字符做爲類型的標識,能夠定義泛型類、泛型接口、泛型方法等。函數

class A<T>{
    T a;

    public <Q> void foo(Q a){

    }
}
interface B<T>{
    void foo(T a);
}

實現

Java的泛型並不像C++那樣是在編譯時根據須要按照模板實例化對應的類。好比下面這段C++代碼,會以A爲模板實例化兩個類。this

template<typename T>
class A 
{
public:
    A(){}
    T a; 
};
int main()
{
    A<int> a;
    A<float> b;

    return 0;
}

查看彙編能夠證明,調用的是兩個不一樣類的構造函數code

0x0000000000400545 <+15>:    callq  0x40056c <A<int>::A()>                                      
   0x0000000000400551 <+27>:    callq  0x400578 <A<float>::A()>

Java中全部類都繼承自Object,任意類對象均可以向上轉型後,使用Object變量存儲其引用。基於這一點,Java的泛型實現時其實只有一種類型。如下兩個類實際是等同的對象

class A<T>{
    T a;
}
class A{
    Object a;
}

當使用反射機制獲取泛型類的信息時,能夠發現class A<T>實際就是class A繼承

public static void main(String[] args) {
    A<Integer> a = new A();
    Field[] f = a.getClass().getDeclaredFields();
    System.out.println(Arrays.toString(f));
}
// 輸出爲 [java.lang.Object A.a]

由此咱們也能夠知道爲何下面這段代碼老是輸出same class接口

public static void main(String[] args) {
    A<Integer> a = new A();
    A<Double> b = new A();
    if( a.getClass() == b.getClass() ){
        System.out.println("same class");
    }
}

由於基礎類型並不繼承自Object,因此Java的泛型是不支持基礎類型的。若是這麼作了,就會獲得一個錯誤提示Type argument cannot be of primitive typeget

自限定類型

由於使用泛型時,其類型參數會被當作Object來處理,因此編譯器就沒法感知真實類型的方法了。
好比下面這段代碼,就沒法經過編譯編譯器

class A{
    public void foo(){
        System.out.println("A.foo()");
    }
}

class B<T>{
    T a;
    B(T a){
        this.a = a;
    }
    public bar(){
        a.foo();    // 此處會提示編譯錯誤,Object類型不存在foo()方法
    }
}

此時必須將泛型類B的類型參數作限定,讓編譯器能從限定中獲取到足夠的信息去判斷類型參數是存在foo()方法的。

class B<T extends A>{
    T a;
    B(T a){
        this.a = a;
    }
    public bar(){
        a.foo();    
    }
}
相關文章
相關標籤/搜索