標籤:參數化 父類 swa style st3 fat 本質 泛型 子類
首先看一看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實現了List1List3<Father> b2 = new List4<Son, Father>();//List4繼承了List3,所以List3是父類,可以指向子類對象。
雖然這樣也完成了泛型類的繼承,實現了和普通類一樣的多態,但是使用起來並不是特別好,就這樣java引入了萬用字元概念:
萬用字元上限:
/*萬用字元的上限:Pair<? extends Father> c2extends是關鍵字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;errorc3 = 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); }
注意:萬用字元的捕獲只有在許多限制的情況下才是合法的,編譯器必須能夠確信萬用字元表達的是單個,確定的類型。
java泛型類的繼承規則