(1) 若是你定義了一個泛型(類、接口),那麼Java規定,你不能在全部的靜態方法、靜態初塊等全部靜態內容中使用泛型的類型參數。例如:java
public class A<T> { public static void func(T t) { //報錯,編譯不經過 } }
(2) 如何在靜態內容(靜態方法)中使用泛型,更通常的問題是,若是類(或者接口)沒有定義成泛型,可是就想在其中某幾個方法中運用泛型(好比接受一個泛型的參數等),該如何解決?安全
public class A<T> { ... }
代表在類A的做用域中,T是泛型類型參數。public static <T, S> int func(List<T> list, Map<Integer, S> map) { ... }
,其中T和S是泛型類型參數。
(3) 類型參數的做用域code
class A<T> { ... }
中T的做用域就是整個A;public <T> func(...) { ... }
中T的做用域就是方法func;對象
類型參數也存在做用域覆蓋的問題,能夠在一個泛型模板類/接口中繼續定義泛型方法,例如:接口
class A<T> { // A已是一個泛型類,其類型參數是T public static <T> void func(T t) { // 再在其中定義一個泛型方法,該方法的類型參數也是T } } //當上述兩個類型參數衝突時,在方法中,方法的T會覆蓋類的T,即和普通變量的做用域同樣,內部覆蓋外部,外部的同名變量是不可見的。 //除非是一些特殊需求,必定要將局部類型參數和外部類型參數區分開來,避免發生沒必要要的錯誤,所以通常正確的定義方式是這樣的: class A<T> { public static <S> void func(S s) { } }
(4) 泛型方法的類型參數能夠指定上限,類型上限必須在類型參數聲明的地方定義上限,不能在方法參數中定義上限。規定了上限就只能在規定範圍內指定類型實參,超出這個範圍就會直接編譯報錯。作用域
<T extends X> void func(List<T> list){ ... }
,正確<T extends X> void func(T t){ ... }
,正確<T> void func(List<T extends X> list){ ... }
,編譯錯誤(1) 顯式指定方法的類型參數,類型參數要寫在尖括號中並放在方法名以前。例如:object.<String> func(...)
,這樣就顯式指定了泛型方法的類型參數爲String,那麼全部出現類型參數T的地方都將替換成String類型。編譯器
(2) 隱式地自動推斷,不指明泛型參數,編譯器根據傳入的實參類型自動推斷類型參數。例如:<T> void func(T t){ ... }
隱式調用object.func("name")
,根據"name"
的類型String推斷出類型參數T的類型是Stringio
(3) 避免歧義,例如:<T> void func(T t1, T t2){ ... }
若是這樣調用的話object.func("name", 15);
雖然編譯不會報錯,可是仍然會有很大隱患,T到底應該是String仍是Integer存在歧義;編譯
(4) 有些歧義Java是會直接當成編譯錯誤的,即全部和泛型參數有關的歧義,例如:<T> void func(List<T> l1, List<T> l2){...}
若是這樣調用的話,object.func(new List<String>(), new List<Integer>());
這裏會有歧義,編譯器沒法知道T到底應該是String仍是Integer,這種歧義會直接報錯的,編譯沒法經過。即泛型方法中,若是類型參數恰好就是泛型參數的類型實參,那麼這個類型實參不得有歧義,不然直接編譯報錯。模板
(1) 你會發現全部能用類型通配符(?)解決的問題都能用泛型方法解決,而且泛型方法能夠解決的更好。
void func(List<? extends A> list);
<T extends A> void func(List<T> list);
(2) 兩種方法能夠達到相同的效果,「?」能夠表明範圍內任意類型,而T也能夠傳入範圍內的任意類型實參,而且泛型方法更進一步,「?」泛型對象是隻讀的,而泛型方法裏的泛型對象是可修改的,即List<T> list
中的list是可修改的。
(3) 二者最明顯的區別
(3) 適用場景
public <T> void func(List<T> list, T t) { list.add(t); }
<T> void func(List<? extends T> list, T t);
即第一個參數依賴第二個參數的類型(第一個參數list的類型參數必須是第二個參數的類型或者其子類)。<T, E extends T> void func(List<T> l1, List<E> l2);
這裏E只在形參中出現了一次(類型參數聲明不算),而且沒有任何其餘東西(方法形參、返回值)依賴它,那麼就能夠把E規約成「?」。規約結果<T> void func(List<T> l1, List<? extends T> l2);
public static <T> void Collections.copy(List<T> dest, List<? extends T> src) { ... }
從src拷貝到dest,那麼dest最好是src的類型或者其父類,由於這樣才能類型兼容,而且src只是讀取,不必作修改,所以使用「?」還能夠強制避免對src作沒必要要的修改,增長的安全性。