最近看Spring 源代碼的時(shí)候遇到了一個(gè)關(guān)于泛型的問題,于是就請(qǐng)教我的老大幫我
解釋了一下,然后又自己看了一點(diǎn)資料,感覺對(duì)泛型已經(jīng)有了一定的理解了,就先
把這些想法記錄下來,供有需要的人參考同時(shí)也為了能夠加深理解
如果你在看JDK1.5的源碼的時(shí)候就會(huì)發(fā)現(xiàn),java.util.HashMap這個(gè)類里面出現(xiàn)了
這種代碼:
public class HashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
{

public HashMap(Map<? extends K, ? extends V> m) {
.}
}
里面出現(xiàn)了大量的角號(hào)和K,V,T...等不明所以的代碼,其實(shí)這就是泛型的應(yīng)用。
泛型的一些概念:
List<E> , List< String > , List
List<E>是參數(shù)話類型 parameterized type ,
E是(formal)類型參數(shù) type parameter,
String 實(shí)際類型參數(shù) actual type argument,
List是自然類型 raw type
為什么會(huì)出現(xiàn)泛型:
原來的JDK中
當(dāng)我不知道user到底是個(gè)什么對(duì)象的時(shí)候只能這樣寫:
static Object maskNull(Object o){
return key == null ? NULL_KEY :key;
}
如果我在外面使用的時(shí)候:
User u = ...;
User user = (User)map.maskNull(u);
這里用到了向上轉(zhuǎn)型,把map.maskNull(u)的返回對(duì)象強(qiáng)制轉(zhuǎn)型為了User,強(qiáng)制轉(zhuǎn)為什么類型完全有程序員控制,所以我在這里這樣寫也是可以的
Department d = (Department)map.maskNull(u);
這個(gè)錯(cuò)誤在編譯的時(shí)候是不會(huì)有任何錯(cuò)誤提示的,必須到了runtime才能知道返回的類型和Department是不匹配的
但是如果使用了泛型以后我可以寫成這樣
static <T> T maskNull(T key) {
return key == null ? (T)NULL_KEY : key;
}
User u = ...;
User user = map.maskNull(u);
當(dāng)我再寫成Department d = map.maskNull(u);
的時(shí)候就會(huì)編譯失敗,因?yàn)榉椒ㄒ髤?shù)和返回值必須是 T 類型的,當(dāng)然這里的T只是暫時(shí)的一個(gè)聲明,類似于去占一個(gè)坑,當(dāng)我需要是User類型的時(shí)候,我就用 User去替換掉它,這樣編譯的時(shí)候就要求傳進(jìn)的和返回的必須是一致的類型,所以我們剛才的那種寫法就不行了,這樣編譯器就使用自身的機(jī)制保證類型安全type-safe
但是泛型還存在另外一個(gè)重要的概念bridge method
先講一下JDK對(duì)泛型的支持:上面的講解雖然說泛型很有用,但是泛型在runtime是沒有任何作用的,只是為了編譯期的方便,所以當(dāng)過了編譯期之后還是要轉(zhuǎn)換為普通代碼的執(zhí)行方式的,也就是說過了編譯期,帶角號(hào)的部分會(huì)被抹掉,變成了原始的方式
static Object maskNull(Object o){...}
那么現(xiàn)在看下面的代碼:
class A<T>{
T setX(T t){...}
}
class B extends A<String>{
String setX(String s){...}
}
很明顯,當(dāng)B的方法void String setX(String s)重載了A的方法,但是按照我們剛才講的理論A被編譯成了
class A{
Object setX(Object t){...}
}
也就是說經(jīng)過編譯期之后類B的方法就不重載了A的方法,這是不被允許的,所以載編譯的時(shí)候同樣有兩外一件事情發(fā)生:JDK會(huì)幫你添加一個(gè)幫助方法來重載A的方法,于是B就變成了:
class B extends A{
String setX(String s){...}
Object setX(Object o){
return setX((String)o);
}
}
大家看到多了個(gè)重載A類方法的一個(gè)方法:
Object setX(Object o){...}
這個(gè)方法就是所謂的bredge method
這個(gè)工作是在編譯期完成的。所以如果你遍歷A上的方法的時(shí)候如果發(fā)現(xiàn)多了方法千萬不要感覺以外
注意這個(gè)方法的重載對(duì)EL表達(dá)式和TPL的運(yùn)行不存在影響,因?yàn)椴还苡玫哪膫€(gè)方法,經(jīng)過TPL的轉(zhuǎn)型之后結(jié)果都是一樣的