java零基礎(chǔ)入門-高級特性篇(六) 泛型? 中
泛型的使用位置窟社,除了最常見的約束集合元素绪钥,還可以使用在接口昧识,類,方法上面缀去。最本質(zhì)的原因就是為了在使用接口甸祭,類池户,方法的時候凡怎,可以將類型作為參數(shù)统倒,進行類型的參數(shù)傳遞氛雪。這樣可以使程序的編寫更加的靈活报亩,在創(chuàng)建對象,調(diào)用方法的時候動態(tài)的指定類型岳链,所以泛型也可以理解為類型的參數(shù)化劲件。
類型參數(shù)化
光看名字寇仓,又不好理解烤宙,通俗點可以這樣理解躺枕。定義方法,接口的時候可以傳遞參數(shù)罢猪,參數(shù)通常都是指定類型的叉瘩,比如
public void add(Student student)薇缅;
public? void add (Teacher teacher);
這里的student是Student類型的參數(shù),teacher是Teacher?類型的參數(shù)汤徽,他們已經(jīng)指定好類型了谒府。那么類型的參數(shù)化,就是指將類型作為參數(shù)傳遞進方法泰鸡。比如
public void add(E e);
這里的add方法并沒有指定任何一個具體的類型鸟顺,而是將類型也作為了參數(shù)器虾,E是任何一個類型兆沙,e是任意類型E的實例。如果傳遞進來的類型參數(shù)是Student千扔,那么這個方法就是add(Student student);库正,如果傳遞進來的類型參數(shù)是Teacher 褥符,那么這個方法就是add?(Teacher teacher);
當(dāng)使用泛型定義參數(shù)的時候喷楣,每一個傳遞進來的類型參數(shù),就創(chuàng)建了一個該方法的版本逊朽,add(Student student)是一個add(E e)的版本曲伊,add?(Teacher teacher)也是一個add(E e)版本坟募。類型參數(shù)化的好處是使代碼變得更加靈活婿屹,原因就在于此,因為可以通過對類型的抽象届腐,使代碼匹配各種不同有具體類型版本的需求铁坎。
泛型接口和泛型類
泛型接口的定義,public interface man<T>{...}犁苏。在接口名后面加上泛型類型參數(shù)T硬萍,這樣就定義了一個泛型接口。
在接口中定義的類型參數(shù)可以在接口中當(dāng)做類型使用围详,任何需要類型的地方都可以使用類型參數(shù)替代朴乖。比如傳遞的類型是Teacher,那么run(T t)就是老師在跑路助赞,getObject()方法返回一個老師對象买羞,getAll(String name)方法可以根據(jù)學(xué)校名字獲取所有老師雹食。加入傳遞的是Student畜普,那么上面三個方法分別是學(xué)生在跑路,獲取一個學(xué)生對象群叶,根據(jù)學(xué)校名稱返回所有學(xué)生吃挑。使用泛型接口,可以在實現(xiàn)的時候才定義具體需要實現(xiàn)的類型街立,使接口可以進行更高級的抽象舶衬。
泛型類的定義,public class Man<T>{...}赎离,在類名后面加上泛型類型參數(shù)T逛犹,這樣就定義了一個泛型類。
和泛型接口不同蟹瘾,類有構(gòu)造器圾浅,并且構(gòu)造器也可以使用泛型類型參數(shù)掠手。在這個泛型類里面憾朴,使用了兩個泛型類型參數(shù),如果有必要可以定義更多的泛型參數(shù)喷鸽。
如果java里面沒有繼承這個特性众雷,那么泛型到這里就講完了,但是做祝,正因為java有繼承這個特性砾省,會導(dǎo)致很多其他的問題出現(xiàn),其復(fù)雜程度會幾何級的上升混槐,后面的知識點對抽象能力和思維能力有較高的要求编兄,請做好戰(zhàn)斗準(zhǔn)備。
下面從集合開始声登,先來思考幾個前面沒有思考過的問題狠鸳。
1.如果集合加上了泛型揣苏,那么如果添加的元素是泛型的子類或者父類能添加進去嗎?
上面例子可以看出件舵,如果泛型類型有子類卸察,添加泛型類型的子類是可以的,但是如果泛型類型有父類铅祸,往集合添加泛型類型的父類會出現(xiàn)編譯錯誤坑质。因為子類繼承了父類的所有方法,所以如果添加的是子類临梗,當(dāng)從集合取出的元素調(diào)用泛型類型的方法也不會有什么問題涡扼。但是如果定義的是子類的泛型集合,放入的是父類元素盟庞,當(dāng)要使用子類方法的時候壳澳,父類元素可能沒有,那么就會發(fā)生錯誤茫经,所以泛型是子類型的話巷波,是不允許加入父類型元素的。
2.再看另一個問題卸伞,如果父類是泛型類型抹镊,如何定義子類?
如果將一個類定義為泛型類荤傲,那么在創(chuàng)建該泛型類的子類的時候不能將子類直接繼承該泛型類垮耳,而是需要指定父類泛型的類型。比如父類是Book<T>遂黍,子類不能直接extends Book<T>终佛,而是需要指定T的類型,上例中使用的Book<Double>作為類型雾家。
在java中铃彰,泛型不能繼承和實現(xiàn)。為什么芯咧?WHY牙捉?請手動滑動到本章最上面,跟我一起念敬飒,類型參數(shù)化邪铲。問題的關(guān)鍵就在這里,因為泛型將類型作為一種參數(shù)无拗,而參數(shù)是什么带到?在定義方法的時候,他不需要具體指定是什么數(shù)據(jù)英染,但是一旦你調(diào)用使用這個方法揽惹,就必須指定這個參數(shù)具體是什么晌纫。
由于方法中的泛型需要在定義類的時候就指定,所以如果需要使用含有泛型的方法永丝,必須在創(chuàng)建該泛型類對象的時候就需要指定泛型類型锹漱,因為使用的時候必須指定類型,不論是普通參數(shù)還是泛型參數(shù)慕嚷。那為什么繼承的時候也要確定泛型呢哥牍?因為繼承就是在使用一個已經(jīng)定義好的類,使用泛型類喝检,就要指定類型嗅辣。
3.用什么樣的參數(shù)形式來接受List<Book>這種形式的參數(shù)?
現(xiàn)在需要為所有List抽象一個方法挠说,不論給的參數(shù)是List<Book>澡谭,List<String>,都可以接收并且打印List中的元素损俭。是不是理所當(dāng)然的想到了List<Object>蛙奖?用List<Object>來接收參數(shù)就行了嘛。
啪啪啪杆兵,臉是不是很疼雁仲。顯然這樣是不可以的,錯誤提示參數(shù)類型不匹配琐脏,Object是所有類型的父類攒砖,但是List<Object>并不是List<Book>的父類,那應(yīng)該使用什么方法達到上面的要求呢日裙?泛型提供了一個泛型通配符用于接收所有類型的泛型類型吹艇。
泛型的通配符
泛型的通配符可以很好的解決所有泛型類型父類的問題,使用<?>來作為類或接口的泛型參數(shù)昂拂,這樣就可以抽象出泛型類的父類受神。比如用List<?>可以看做所有List的父類,Set<?>可以看做所有Set的父類政钟,使用路克?來表示一個未定義的類型,用來接受任何類型參數(shù)养交。
但是如果使用通配符,在部分功能上是會受到限制的瓢宦。
1.只能通過Object遍歷集合碎连。在訪問通配符泛型List<?>的時候,集合里的元素只能當(dāng)做Object來訪問驮履,因為在定義的時候只是一個通配符鱼辙,不是具體類型廉嚼,所以不能進行類型轉(zhuǎn)換只能作為Object訪問。
2.不能使用add方法倒戏。List提供的add(E e)方法是需要指定類型的怠噪,這里不是E嗎?這是個泛型類型岸捧巍傍念?為什么要提供類型?因為這是定義葛闷,一旦要使用add(E e)方法憋槐,必須指定具體的類型。定義通配符以后淑趾,在使用通配符的方法里是不知道類型的阳仔,所以不能使用add方法。
就算是Object類型也不能使用add方法扣泊,為什么近范?假設(shè)可以添加,會發(fā)生什么問題延蟹?如果我使用List<Book>作為參數(shù)顺又,傳入到printAllObject方法,運行完打印元素的語句后等孵,會往List<Book>類型的集合里面新增一個Object類型的對象稚照,而Object又是Book類型的父類,上面說過俯萌,泛型類型的父類型元素不能添加到該集合果录,所以這里就算是Objcet類型也不能添加。所以使用泛型通配符的話咐熙,這個集合的作用就是使用Object類型來遍歷它弱恒。
上面第二點,如果集合使用了泛型通配符棋恼,要往集合添加Object是不允許的返弹,因為無論最后來的是什么類型,Object都是這個類型的父類爪飘,所以不允許添加Object類型义起。那么如果我可以保證添加一個元素,一定是泛型類型的子類师崎,那么是不是可以添加元素了默终?這個問題就涉及到泛型通配符的上下限問題了。下章繼續(xù)。
本章有很多類名稱相同齐蔽,但是內(nèi)容不同两疚,請在不同的包下進行操作。