解析java泛型(一)

?????對(duì)于我們java中的泛型,可能很多人知道怎么使用并且使用的還不錯(cuò),但是我認(rèn)為想要恰到好處的使用泛型,還是需要深入的了解一下它的各種概念和內(nèi)部原理。本文將盡可能的囊括java泛型中的重要的概念。主要內(nèi)容如下:

  • 泛型的定義及為什么要使用泛型
  • 定義一個(gè)簡(jiǎn)單的泛型類
  • 定義一個(gè)簡(jiǎn)單的泛型方法
  • 類型參數(shù)的限定
  • 泛型內(nèi)部實(shí)現(xiàn)的基本原理
  • 泛型通配符(難點(diǎn))
  • 泛型的其他實(shí)現(xiàn)細(xì)節(jié)

一、何謂泛型
?????泛型程序設(shè)計(jì)意味著編寫(xiě)的代碼可以被不同中類型的對(duì)象重用。例如:MyList<T>,MyList<Integer>是一種類型,MyList<String>也是一種類型,但是使用的代碼塊都是MyList<T>,這也就是java中引入泛型的一種原因:可以增強(qiáng)代碼的復(fù)用性,當(dāng)然這種限定死類型的方式也會(huì)使得代碼的安全性和可讀性更高。

二、一個(gè)簡(jiǎn)單的泛型類
先看一個(gè)完整的泛型類:

/*一個(gè)簡(jiǎn)單的泛型類的聲明如下*/
public class Pair<T> {
    private T a;
    private T b;
    public Pair(T a, T b){
        this.a = a;
        this.b = b;
    }
}

?????由此可以看出來(lái),泛型類型和普通類型的區(qū)別主要在于:類名之后多了個(gè)<T>,并且實(shí)例域類型可以不是具體的類型而是不確定的T類型。其中,我們管T叫做類型變量,類型變量一般使用大寫(xiě)字母表示并且很短(在java中使用E表示集合的元素類型,K和V分別表示關(guān)鍵字和值的類型)。
使用具體的類型來(lái)替換類型變量的過(guò)程我們叫做實(shí)例化泛型類型。例如:Pair<Integer>,<Double>等。這其中需要注意的是:java中的9中基本類型是不能作為類型變量的,也就是:Pair<int>,是會(huì)不允許的。當(dāng)然,聲明一個(gè)泛型類時(shí),不局限于一個(gè)類型變量,可以由多個(gè)類型變量,例如:

/*聲明兩個(gè)類型變量也是可以的*/
public class Pair<T,U> {
    private T a;
    private U b;
    public Pair(T a, U b){
        this.a = a;
        this.b = b;
    }
}
//Pair<String,Integer> p new Pair<>("abc",12);
//創(chuàng)建泛型類實(shí)例變量的時(shí)候,可以省略類型變量,編譯器可以推測(cè)出來(lái)

三、一個(gè)簡(jiǎn)單的泛型方法
?????怎么定義泛型類,我們已經(jīng)介紹過(guò)了,接下來(lái)我們一起看看泛型方法是如何定義和調(diào)用的。

/*泛型類中定義了一個(gè)泛型方法*/
public class Pair<T> {
    //聲明一個(gè)泛型方法
    public <T> T getA(T c){
        return c;
    }
}
/*main函數(shù)中調(diào)用泛型方法*/
public class Test2 {
    public static void main(String[] args){
        Pair<Integer> p = new Pair<Integer>(1,2);
        //調(diào)用泛型方法
        System.out.println(p.<Integer>show(10));
    }
}

?????我們可以看到,聲明一個(gè)泛型方法:public <T > T getA(T c),<T >放在返回值前面,修飾符后面,T表示返回類型。泛型方法的調(diào)用:p.<Integer>show(10),在方法名前面放置類型變量,當(dāng)然也可以選擇省略,當(dāng)編譯器沒(méi)有足夠的信息推測(cè)出來(lái)時(shí)就會(huì)報(bào)錯(cuò),那時(shí)你再添加也不遲(但是,如果你能減輕計(jì)算機(jī)的工作的話,想必是可以提高效率的)
?????小結(jié)一下,泛型類和泛型方法。泛型類中可以聲明泛型方法也可以聲明普通方法,泛型方法可以出現(xiàn)在泛型類中也可以出現(xiàn)在普通類中,也就是它們之間并沒(méi)有什么約束關(guān)系。
四、類型變量的限定
前面我們已經(jīng)知道了什么是類型變量,我們看一段代碼:

public class Pair<T> {
    public static <T> int myCompare(T a,T b){
        return a.compareTo(b);//此處編譯不通過(guò)
    }
}

?????我們知道,如果想要使用compareTo方法,就要實(shí)現(xiàn)Comparable接口,或者繼承實(shí)現(xiàn)了此接口的類。此處想要使得程序正確,有兩種辦法。第一種:使類繼承Comparable接口并且實(shí)現(xiàn)compareTo方法。第二種:就是使用類型變量限定。如下:

/*限定變量類型*/
public class Pair<T> {
    public static <T extends Comparable> int myCompare(T a,T b){
        return a.compareTo(b);
    }
}

?????細(xì)心的同學(xué)可能已經(jīng)發(fā)現(xiàn),相比于原來(lái)的方法,就是使類型變量繼承與Comparable接口。原來(lái)的<T >變成了<T extends Comparable>,表示:原來(lái)的T可以是任意類型的,而現(xiàn)在的T被限制必須實(shí)現(xiàn)了Comparable 接口,就是說(shuō),凡是使用此泛型的類都是直接或者間接繼承了Comparable 接口并實(shí)現(xiàn)其中方法的。所以,一旦我們將T限定了,就不用考慮實(shí)現(xiàn)Comparable 接口的事情了,程序的封裝性更強(qiáng)了。
?????對(duì)類型變量的限定可以由多個(gè)限定,它們之間使用&分隔,而使用逗號(hào)分隔類型變量??磦€(gè)例子:

<T extends Comparable>    //一個(gè)類型變量的一個(gè)類型限定

<T extends Comparable & Serializable> //一個(gè)類型變量的兩個(gè)類型限定

<T extends Comparable,U extends Serializable>//兩個(gè)類型變量的類型限定

五、泛型實(shí)現(xiàn)的基本原理
?????討論了這么多的泛型方法,泛型類以及各種使用技巧,接下來(lái),我們一起看看虛擬機(jī)實(shí)際執(zhí)行時(shí)是怎么對(duì)待我們的泛型的。我們都知道java中有編譯器和虛擬機(jī),但實(shí)際上我們的泛型在這兩者看來(lái)是不一樣的,也就是說(shuō),虛擬機(jī)是不認(rèn)識(shí)泛型的,而只有我們強(qiáng)大的編譯器是認(rèn)識(shí)泛型的。那他們是怎么實(shí)現(xiàn)統(tǒng)一的呢?接下來(lái)我們?cè)敿?xì)來(lái)看。
?????在java中,無(wú)論何時(shí)定義了一個(gè)泛型,它都會(huì)自動(dòng)生成一個(gè)相應(yīng)的原始類型。我們叫這個(gè)過(guò)程為:類型擦除。例如下面的代碼:

/*這是一段泛型類的代碼*/
public class Pair<T> {
    private T a;
    private T b;

    public T getA(){
        return this.a;
    }
    
    public T getB(){
        return this.b;
    }
}
經(jīng)過(guò)類型擦除之后生成原始類型:
public class Pair{
    private Object a;
    private Object b;

    public Object getA(){
        return this.a;
    }
    
    public Object getB(){
        return this.b;
    }
}

?????經(jīng)過(guò)對(duì)比,我們可以得出結(jié)論:去除了泛型的標(biāo)志性符號(hào)<>并且所有的T類型都被替換成Object類型了。難道我們的類型擦除就是將所有的未知類型轉(zhuǎn)換為Object類型嗎?當(dāng)然不是,類型擦除是有規(guī)則的而不是一味的將未知類型T轉(zhuǎn)換成Object類型的。
?????對(duì)于有限定的類型變量就將用類型變量的第一個(gè)限定類型替換。如:Pair<T extends Comparable & Serializable>,就會(huì)選擇用Comparable替換所有的T并去除修飾在類后面的泛型符號(hào),生成原始類型。
?????對(duì)于沒(méi)有限定類型的類型變量就默認(rèn)使用Object替換類型變量。例如:Pair<T>就會(huì)使用Object替換所有的T類型變量。
?????最后小結(jié)一下,類型擦除針對(duì)是否有類型限定類型,根據(jù)不同的狀況進(jìn)行替換生成相應(yīng)的原始類型供jvm調(diào)用。
未完,待續(xù)。。。。

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

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

  • 一、為什么要使用泛型 1.類型參數(shù)的好處 類型安全:泛型的主要目標(biāo)是提高 Java 程序的類型安全。通過(guò)知道使用泛...
    SeanMa閱讀 7,289評(píng)論 1 18
  • 引言:泛型一直是困擾自己的一個(gè)難題,但是泛型有時(shí)一個(gè)面試時(shí)老生常談的問(wèn)題;今天作者就通過(guò)查閱相關(guān)資料簡(jiǎn)單談?wù)勛约簩?duì)...
    cp_insist閱讀 1,940評(píng)論 0 4
  • 前言 JDK1.5號(hào)稱是Java最重要的版本更新,而泛型又是JDK1.5中一個(gè)最重要的特征。使用泛型機(jī)制編寫(xiě)的程序...
    WinsonWu閱讀 673評(píng)論 0 0
  • 泛型的好處 使用泛型的好處我覺(jué)得有兩點(diǎn):1:類型安全 2:減少類型強(qiáng)轉(zhuǎn) 下面通過(guò)一個(gè)例子說(shuō)明: 假設(shè)有一個(gè)Tes...
    德彪閱讀 1,216評(píng)論 0 0
  • 《活法》中一小節(jié)”利他” 利他之心,在佛教就是與人為善的慈悲心。在基督教就是愛(ài)?,F(xiàn)在說(shuō)的樸實(shí)...
    徐書(shū)軍閱讀 207評(píng)論 0 0

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