事情要從一次面試說起,面試官問了這么一個(gè)問題,在JDK下面這個(gè)方法中:
public static <T extends Comparable<? super T>> void sort(List<T> list)
這里面<T extends Comparable<? super T>>有什么用?
美好的愿景
很明顯,從語義上來說,sort方法要對一個(gè)List排序,這個(gè)List中的元素類型為T,sort方法要求這個(gè)T類型必須是可比較的。這個(gè)可比較的含義是說,任意兩個(gè)T類型的對象,可以通過compareTo方法來確定大小。理想情況下,一個(gè)T類型實(shí)現(xiàn)了Comparable接口的話,那么對于任意兩個(gè)屬于T類型的對象x和y,都可以通過compareTo方法來確定大小。因此,方法寫成如下形式即可。
public static <T extends Comparable> void sort(List<T> list)
Comparable接口的無奈
可是現(xiàn)實(shí)世界里,并不是T類型實(shí)現(xiàn)了Comparable接口就可以和T類型比較,而是T類型實(shí)現(xiàn)Comparable<E>之后,可以和E比較。盡管T實(shí)現(xiàn)Comparable<E>毫無道理可言,但是在語法上是正確的。
所以對于下面這個(gè)類,只能說代碼寫得爛,作者職業(yè)素養(yǎng)低,而編譯器卻無能為力,令人扼腕嘆息。
public class Bar implements Comparable<String>
Java泛型系統(tǒng)出來背一次鍋
為了將這樣的類拒之門外,sort方法寫成下面這樣豈不是很好?sort需要的T類型,是一個(gè)能和T類型比較的類型,真是天衣無縫啊。
public static <T extends Comparable<T>> void sort(List<T> list)
但是有這樣一種情況,假設(shè)有個(gè)類型S,S實(shí)現(xiàn)了Comparable<S>,然后T繼承了S,那么T就具備了和S比較的能力。但是這時(shí)T能通過編譯么?答案是不能,因?yàn)樵诰幾g器看來,T只具備和S比較的能力,不具備和T比較的能力。盡管T是S的子類,但是編譯器不認(rèn)為如果一個(gè)類型具備了和S比較的能力,就具備了和T比較的能力。聽上去感覺有點(diǎn)不合理,就好比經(jīng)常有獵頭問我:“好了,我現(xiàn)在知道你會用hadoop、spark這些來搞大數(shù)據(jù)了,那么請問你會Java么?”
這個(gè)就是Java泛型系統(tǒng)的一個(gè)坑特性——不具備協(xié)變、逆變的能力,所以盡管Integer是Number的子類,List<Integer>卻不是List<Number>子類,一個(gè)能比較Number的比較器也不能被當(dāng)做一個(gè)Integer的比較器。
所以最終用<T extends Comparable<? super T>>對T進(jìn)行了約束,這樣不管T的Comparable是自己實(shí)現(xiàn)的也好,還是繼承的也好,都可以海納百川有容乃大的被sort方法接受了。
美中不足
但是<T extends Comparable<? super T>>約束能力還是不夠強(qiáng),因?yàn)樗谡Z義上表示的是“T能夠和super T的任何類型比較”,所以下面這個(gè)類還是可以編譯通過的,除了你的同事會對你的編程水平和職業(yè)素養(yǎng)產(chǎn)生質(zhì)疑。
public class Bar extends RuntimeException implements Comparable<Exception>
就像equals和hashcode的代碼契約一樣,如果編譯器在語法層面完全無法提供檢查,只能靠程序員的職業(yè)素養(yǎng)來產(chǎn)生良好的代碼,真是令人太不開心了。