其實(shí)關(guān)于這塊知識(shí)大概兩年以前就有了個(gè)比較全的了解了吧,重新整理一下用自己的話寫出來又是一次加深理解的過程。下面我大概會(huì)分三個(gè)方面去講泛型,1、泛型的主要相關(guān)特性 2、怎么用泛型 3、泛型總結(jié)
1、泛型的主要相關(guān)特性
背景介紹:
Java泛型(generics)是JDK 5中引入的一個(gè)新特性,允許在定義類和接口的時(shí)候使用類型參數(shù)(type parameter)。聲明的類型參數(shù)在使用時(shí)用具體的類型來替換。泛型最主要的應(yīng)用是在JDK 5中的新集合類框架中。對(duì)于泛型概念的引入,開發(fā)社區(qū)的觀點(diǎn)是褒貶不一。從好的方面來說,泛型的引入可以解決之前的集合類框架在使用過程中通常會(huì)出現(xiàn)的運(yùn)行時(shí)刻類型錯(cuò)誤,因?yàn)榫幾g器可以在編譯時(shí)刻就發(fā)現(xiàn)很多明顯的錯(cuò)誤。而從不好的地方來說,為了保證與舊有版本的兼容性,Java泛型的實(shí)現(xiàn)上存在著一些不夠優(yōu)雅的地方。當(dāng)然這也是任何有歷史的編程語言所需要承擔(dān)的歷史包袱。后續(xù)的版本更新會(huì)為早期的設(shè)計(jì)缺陷所累。
a、類型擦除
正確理解泛型概念的首要前提是理解類型擦除(type erasure)。 Java中的泛型基本上都是在編譯器這個(gè)層次來實(shí)現(xiàn)的。在生成的Java字節(jié)代碼中是不包含泛型中的類型信息的。使用泛型的時(shí)候加上的類型參數(shù),會(huì)被編譯器在編譯的時(shí)候去掉。這個(gè)過程就稱為類型擦除。還是上例子吧,如下:

按照我們對(duì)于重載的理解,方法參數(shù)類型不一樣應(yīng)該是可以定義的啊,這個(gè)理論一直是成立的。主要原因是在于泛型是在編譯的時(shí)候類型會(huì)擦除,這樣兩個(gè)方法的參數(shù)類型都是List,是不帶參數(shù)的噢。所以重載不通過導(dǎo)致編譯不通過。
b、泛型在初始化、實(shí)例化的時(shí)候需要知道確切的類型
泛型在沒有申明具體的類型的時(shí)候是不能進(jìn)行實(shí)例化,因?yàn)樗诖藭r(shí)只是一個(gè)代號(hào),換句話說我創(chuàng)建一個(gè)對(duì)象的過程要經(jīng)歷,加載、連接、初始化、實(shí)例化。我連你的基本信息都不知道我怎么進(jìn)行下去啊,上例子,如下:

這種情況下new T()編譯失敗。
c、泛型和子類型
我們知道在java中存在 父類 ?a = new 子類(); 這種寫法是成立的而且是一個(gè)比較好的寫法。但是List<父類> list = new ArrayList<子類>(); 這種寫法會(huì)成立嗎,事實(shí)是沒有成立編譯會(huì)報(bào)錯(cuò),上例子:

為什么會(huì)這樣呢,應(yīng)該這么理解如果這種寫法成立的話,那我fruits就可以add ?Child2、Child3等其他子類了。
到時(shí)候我從fruits里面取出來集合里面的元素的時(shí)候我應(yīng)該轉(zhuǎn)換成Child、Child2、Child3 ??? 編譯器也不知道啊。
d、泛型通配符
我想在通配符這個(gè)點(diǎn)我會(huì)多講一點(diǎn)。
d1、通配符?
這里使用了通配符?指定可以使用任何類型的集合作為參數(shù)。讀取的元素使用了Object類型來表示,這是安全的,因?yàn)樗械念惗际荗bject的子類。這里就又出現(xiàn)了另外一個(gè)問題,如下代碼所示,如果試圖往使用通配符?的集合中加入對(duì)象,就會(huì)在編譯時(shí)出現(xiàn)錯(cuò)誤。需要注意的是,這里不管加入什么類型的對(duì)象都會(huì)出錯(cuò)。這是因?yàn)橥ㄅ浞??表示該集合存?chǔ)的元素類型未知,可以是任何類型。往集合中加入元素需要是一個(gè)未知元素類型的子類型,正因?yàn)樵摷洗鎯?chǔ)的元素類型未知,所以我們沒法向該集合中添加任何元素。唯一的例外是null,因?yàn)閚ull是所有類型的子類型,所以盡管元素類型不知道,但是null一定是它的子類型。另一方面,我們可以從List lists中獲取對(duì)象,雖然不知道List中存儲(chǔ)的是什么類型,但是可以肯定的是存儲(chǔ)的類型一定是Object的子類型,所以可以用Object類型來獲取值。如for(Object obj: lists),這是合法的。
還是上代碼:

d2、?extends通配符
其實(shí)這個(gè)也好理解,可以理解為某個(gè)接口或者某個(gè)抽象類的所有實(shí)現(xiàn)類,上例子:

List<? extends Serializable> 中存放的是所有的Serializable的實(shí)現(xiàn)類,如可能是String、Integer等
我們?cè)谌〕鰜淼臅r(shí)候就可以轉(zhuǎn)換成Serializable ,但是我們還是不能往里面add元素原因跟上面通配符 ?一樣
d3、?super通配符
這里還有一種邊界通配符為?super,可以表示當(dāng)前類或者當(dāng)前類的父類,還是上例子吧:

e、泛型的推斷
自Java SE 7起, 你可以使用一個(gè)空的類型參數(shù)集合 (<>)代替構(gòu)造器的參數(shù)化類型:Map<String, List> myMap = new HashMap<>();注意:想要在泛型類初始化期間利用自動(dòng)類型推斷。
2、怎么用泛型
上面主要介紹了一些泛型的主要特性吧,下面我還是想說說怎么使用泛型吧。
泛型用自己的話來說就是“參數(shù)化類型”,在各種實(shí)體類型的基礎(chǔ)上抽象出一個(gè)虛擬的類型,你可以叫A、B、C、T等。
具體的使用可以有兩種比較常見的表現(xiàn)形式,類泛型 、方法泛型
a、類泛型
從字面上理解為你定義的泛型是整個(gè)類通用的或者是類里面所有的泛型類型就引用類上的自己方法上不額外定義,例子如下:


從上面可以看出類中定義了請(qǐng)求類型為Request返回類型為Response這個(gè)為了可讀性強(qiáng)一點(diǎn)這么定義,在process抽象方法中直接用Request,Response來表示方法所需要的類型。
在實(shí)現(xiàn)類中就可以用具體的String、Integer代替接口中申明的泛型類型。
b、方法泛型
方法泛型我感覺就沒類泛型那么抽象性高,可以為每一個(gè)方法來獨(dú)自定義自己的類型了,上例子:

如上是一個(gè)簡單的將List 里面的實(shí)體用某個(gè)字段作為key轉(zhuǎn)換到Map中,中間的轉(zhuǎn)換過程TODO.
方法上應(yīng)用了K,V兩個(gè)類型,但是這兩個(gè)類型是需要申明定義的,所以在方法修飾符后面要自定義一下類型,如果不定義<K,V>是要編譯出錯(cuò)的。
3、泛型總結(jié)
上面1、2部分介紹了泛型的一些主要特性和常用用法以后,我們對(duì)于泛型的認(rèn)識(shí)應(yīng)該是比較清晰的,所有的哪怕是泛型的復(fù)雜應(yīng)用都是從上面講的一些演變過來了,其實(shí)挺簡單的。什么時(shí)候用泛型這個(gè)需要自己去思考?總之我覺得能夠抽象出一個(gè)統(tǒng)一的類型就可以用。。。
3 Q very much!!!