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

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