一塞绿、為什么需要泛型
? ? 一般的類和方法,只能使用具體的類型:要么是基本的數(shù)據(jù)類型况毅,要么是自定義的類分蓖。如果要編寫可以用于多種數(shù)據(jù)類型的代碼,這種刻板的限制對(duì)代碼的束縛就會(huì)很大尔许。
????定義了一個(gè)List類型的集合么鹤,先向其中加入了兩個(gè)字符串類型的值,隨后加入一個(gè)Integer類型的值母债。這是完全允許的午磁,因?yàn)榇藭r(shí)list默認(rèn)的類型為Object類型。在之后的循環(huán)中毡们,由于忘記了之前在list中也加入了Integer類型的值或其他編碼原因迅皇,很容易出現(xiàn)類似于下面1中的錯(cuò)誤。因?yàn)榫幾g階段正常衙熔,而運(yùn)行時(shí)會(huì)出現(xiàn)“java.lang.ClassCastException”異常登颓。因此,導(dǎo)致此類錯(cuò)誤編碼過(guò)程中不易發(fā)現(xiàn)红氯。
在如上的編碼過(guò)程中框咙,我們發(fā)現(xiàn)主要存在兩個(gè)問(wèn)題:
1.當(dāng)我們將一個(gè)對(duì)象放入集合中咕痛,集合不會(huì)記住此對(duì)象的類型,當(dāng)再次從集合中取出此對(duì)象時(shí)喇嘱,改對(duì)象的編譯類型變成了Object類型茉贡,但其運(yùn)行時(shí)類型任然為其本身類型。
2.因此者铜,處取出集合元素時(shí)需要人為的強(qiáng)制類型轉(zhuǎn)化到具體的目標(biāo)類型腔丧,且很容易出現(xiàn)“java.lang.ClassCastException”異常。而且人為的強(qiáng)轉(zhuǎn)也不利于代碼的復(fù)用作烟。
所以我們需要一個(gè)方法使得我們能夠保證在編譯階段代碼不出問(wèn)題愉粤,運(yùn)行階段代碼就一定不會(huì)出問(wèn)題。而這就是泛型的作用拿撩。
二.什么是泛型
????泛型衣厘,即“參數(shù)化類型”。一提到參數(shù)压恒,最熟悉的就是定義方法時(shí)有形參影暴,然后調(diào)用此方法時(shí)傳遞實(shí)參。那么參數(shù)化類型怎么理解呢涎显?顧名思義坤检,就是將類型由原來(lái)的具體的類型參數(shù)化,類似于方法中的變量參數(shù)期吓,此時(shí)類型也定義成參數(shù)形式(可以稱之為類型形參)早歇,然后在使用/調(diào)用時(shí)傳入具體的類型(類型實(shí)參)。
????采用泛型寫法后讨勤,想加入一個(gè)Integer類型的對(duì)象時(shí)會(huì)出現(xiàn)編譯錯(cuò)誤箭跳,通過(guò)List,直接限定了list集合中只能含有String類型的元素潭千,從而在//2處無(wú)須進(jìn)行強(qiáng)制類型轉(zhuǎn)換谱姓,因?yàn)榇藭r(shí),集合能夠記住元素的類型信息刨晴,編譯器已經(jīng)能夠確認(rèn)它是String類型了屉来。
????有很多的原因促成了泛型的出現(xiàn),而最引人注意的一個(gè)原因就是為了創(chuàng)造容器類狈癞,在我的博客有一篇討論了java容器類茄靠。容器就是存放要使用的對(duì)象的地方。數(shù)組也是如此蝶桶,但是數(shù)組能保存單一的類型慨绳,容器類與數(shù)組想比,容器類更加靈活。具備更多不同的功能脐雪。
在這里我們觀察一下List的接口定義:
? ? 我們可以看到厌小,在List接口中采用泛型化定義之后,中的E表示類型形參战秋,可以接收具體的類型實(shí)參璧亚,并且此接口定義中,凡是出現(xiàn)E的地方均表示相同的接受自外部的類型實(shí)參获询。
自然的涨岁,ArrayList作為L(zhǎng)ist接口的實(shí)現(xiàn)類,其定義形式是:
由此我們可以從源代碼的角度明白為什么上面的示例代碼會(huì)產(chǎn)生編譯錯(cuò)誤吉嚣。
三、自定義泛型接口蹬铺、泛型類和泛型方法
? ? 雖然java為我們提供了眾多的容器類尝哆,但是我們還是會(huì)在變成的過(guò)程中需要只用自定義的泛型類或者使用泛型參數(shù)。
????接口甜攀、類和方法都可以使用泛型去定義秋泄,以及相應(yīng)的使用。在具體使用時(shí)规阀,可以定義泛型接口恒序、泛型類和泛型方法。自定義泛型接口谁撼、泛型類和泛型方法與上述Java源碼中的List歧胁、ArrayList類似。
這個(gè)類根據(jù)傳進(jìn)去的參數(shù)不同而生成不同的Box對(duì)象厉碟,但是無(wú)論傳進(jìn)去的是什么類型喊巍,Box都可以提供getDate的方法,這個(gè)方法不依賴具體的類型而實(shí)現(xiàn)箍鼓。從而使得這個(gè)代碼可以輕松的應(yīng)用于不同的地方崭参。
????在泛型接口、泛型類和泛型方法的定義中款咖,我們經(jīng)常使用T,E,K,V等大寫字母來(lái)代表泛型形參何暮,來(lái)接受來(lái)自己外部對(duì)使用時(shí)傳入的參數(shù)。
將上面的代碼換成下圖:
????在使用泛型類時(shí)铐殃,雖然傳入了不同的泛型實(shí)參海洼,但并沒(méi)有真正意義上生成不同的類型,傳入不同泛型實(shí)參的泛型類在內(nèi)存上只有一個(gè)背稼,即還是原來(lái)的最基本的類型(本實(shí)例中為Box)贰军,當(dāng)然,在邏輯上我們可以理解成多個(gè)不同的泛型類型。
????究其原因词疼,在于Java中的泛型這一概念提出的目的俯树,導(dǎo)致其只是作用于代碼編譯階段,在編譯過(guò)程中贰盗,對(duì)于正確檢驗(yàn)泛型結(jié)果后许饿,會(huì)將泛型的相關(guān)信息擦出,也就是說(shuō)舵盈,成功編譯過(guò)后的class文件中是不包含任何泛型信息的陋率。泛型信息不會(huì)進(jìn)入到運(yùn)行時(shí)階段。
????對(duì)此總結(jié)成一句話:泛型類型在邏輯上看以看成是多個(gè)不同的類型秽晚,實(shí)際上都是相同的基本類型