首先看一看java泛型類的使用:
/** * 一個泛型方法:使程序更加安全 * 並且能被更多的使用 * @author 丁** * * @param <T> */ class Pair<T>{ private T first; private T second; //實例化類型變量 public static<T> Pair<T> makePair(Class<T> cl){ try { return new Pair<>(cl.newInstance(), cl.newInstance()); } catch (Exception e) { return null; } } public Pair(){ first = null; second = null; } public Pair(T first, T second){ this.first = first; this.second = second;} public T getFirst() { return first; } public void setFirst(T first) { this.first = first; } public T getSecond() { return second; } public void setSecond(T second) { this.second = second; } }
class Father { private int age = 40; public void getName(){ System.out.println(age); } } class Son extends Father{ private int age = 12; public void getName() { System.out.println(age); } }
在普通類中:Father aa = new Son();父類是可以用來指向子類的
但是在泛型類中卻不是如此:
Pair<Son> bb = new Pair<>(); Pair<Father> cc = bb;//error
1:雖然Son是Father的子類,但是Pair<T>之間沒有繼承關系:
List<Object> a1 = new ArrayList<>(); List<String> a2 = new ArrayList<>(); //設a1可以等於a2 //a1 = a2; //a1.add(1111);因為是對a1進行操作,所以可以添加Object,a1.add(Object ad); //a2.get(0);報錯,因為a1添加Object進入了a2的空間中,但是a2是String類型,所以報錯
2:可以將參數化類型轉換成一個原始類型:
List<String> a2 = new ArrayList<>(); List a3 = a2;//可以通過編譯,但是后面使用方法時可能會產生類型錯誤! //這時候a3對象時原始類型,所以add(Object obj); a3.add(123);//是對a3進行操作,但是最終結果保存到了a2中,將一個Integer裝入String中 顯然是錯誤的;
3:泛型類可以擴展或實現其他的泛型類:
//泛型接口 interface List1<E>{ } //實現了泛型接口的泛型類 class List2<T, E> implements List1<E>{ } //泛型類 class List3<T>{ } //繼承了其他泛型類的泛型類 class List4<T, E> extends List3<E>{ }
List1<Father> b1 = new List2<Son, Father>();//因為List2實現了List1 List3<Father> b2 = new List4<Son, Father>();//List4繼承了List3,所以List3是父類,可以指向子類對象。
雖然這樣也完成了泛型類的繼承,實現了和普通類一樣的多態,但是使用起來並不是特別好,就這樣java引入了通配符概念:
通配符上限:
/* 通配符的上限:Pair<? extends Father> c2 extends是關鍵字 Pair<T>代表的是某個唯一(具體的泛型類)的泛型類:比如Pair<Son>,Pair<Father> 但是Pair<? extends Father>不是具體的泛型類,它所指的是參數類型為Father的子類的所有泛型類(包括Father) */ Pair<? extends Father> c3; Pair<Father> c1 = new Pair<>(); Pair<Son> c2 = new Pair<>(); //c2 = c1;error c3 = c1; c3 = c2;
需要注意的是:
/* * 使用通配符的上限的問題: * ? extends Father getFirst(); * void setFirst(? extends Father); * 當c2= c1時: * c2.setFirst(Father father);時,會將Father對象添加到Son對象內存中,這是不好的 * 所以使用extends上限時,不能使用setFirst(? extends Father),add(? extends Father)* 等方法。 * 但可以使用getFirst();方法 */
c2.setFirst( new Father(); );//error
通配符下限:
/* * 通配符的下限: * ? super Son * 表示的不是某個具體的泛型類,而是表示參數類型為Son的父類的所有可能的泛型類(包括* * Son)
* 和通配符上限一樣,通配符的超類型限定不能使用getFirst()方法,但可以使用setFirst(XX)方法 */ Pair<Son> c1 = new Pair<>(); Pair<? super Son> c3; c3 = c1;
//另一種超類型限定的寫法
Pair<T extends Comparable<? super T>> c4;
無限定通配符:
//無限定通配符 Pair<Son> c7 = new Pair<>(); Pair<String> c5 = new Pair<>(); //c7 = c5;error,因為他們不是同一種類型 Pair<?> c6 = new Pair<>(); c6 = c5;//Pair<?>是所有的Pair泛型類的父類,Pair<?> c6 = new Pair<xx>();
Pair<?>和Pair的本質不同在於:可以用任意的Object對象調用原始的Pair類的setObject()方法;
通配符的捕獲:
//交換First,Second變量值 public static void swap(Pair<?> p){ ? t = p.getFirst();//error,因為通配符(?)不是類型變量,所以不能直接將?寫入代碼中,利用通配符的捕獲來解決這個問題。 p.setFirst(p.getSecond()); p.setSecond(t); }
//交換First,Second變量值 public static void swap(Pair<?> p){ swapHelper(p);//在調用下面的方法時,類型參數就被捕獲了。 } //利用通配符的捕獲來解決該問題 public static <T> void swapHelper(Pair<T> p){ T t = p.getFirst();//T是具體的某個類型。 p.setFirst(p.getSecond()); p.setSecond(t); }
注意:通配符的捕獲只有在許多限制的情況下才是合法的,編譯器必須能夠確信通配符表達的是單個,確定的類型。