泛型(一)->簡單使用
從上週末到現在陸陸續續看了幾天終於把<< java核心技術 >>泛型看完了,有種豁然開朗的感覺尤其是對於泛型擦除又有了新的認識,趁腦還熱趕緊記錄下來.
關於泛型我準備分兩篇寫,第一篇是關於泛型的使用屬於基礎(必須掌握),第二篇是泛型擦除等等一些問題屬於進階(可選).大家自行選擇.
首先我們要知道泛型的英文是Generic,我曾經被中文版的安卓文檔坑過他給翻譯成一般結果始終不理解.
泛型類的定義
泛型類就是具有一個或者多個類型變量的類.
public class Pair<T> {
private T first;
private T second;
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;
}
}
注意:本篇博客栗子都會用到這個類,後面使用的Pair就是指的這個類.
Pair引入了類型變量T,用<>括起來,放在類名後.泛型類可以有多個類型變量例如
public class Pair<T,U>{...}
類中指定的類型變量可以用來指定方法的返回值類型,成員變量,局部變量的類型.例如
public T getFirst() {...}
用具體的類型替換類型變量就可以實例化泛型類,例如
Pair<String> pair = new Pair<>();
這裏舉個實際的栗子,用靜態的minmax方法遍歷數組並計算最大最小值,用一個Pair對象返回兩個結果
public class ArrayAlg {
public static Pair<String> minmax(String[] a) {
if (a == null || a.length == 0)
return null;
String min = a[0];
String max = a[0];
for (int i = 1; i < a.length; i++) {
if (min.compareTo(a[i]) > 0)
min = a[i];
if (max.compareTo(a[i]) < 0)
max = a[i];
}
return new Pair<String>(min, max);
}
}
public static void main(String[] args){
String[] words = {"crash","have","bad","a"};
Pair<String> pair = ArrayAlg.minmax(words);
System.out.println("min:"+pair.getFirst());
System.out.println("max:"+pair.getSecond());
}
泛型方法
前面已經介紹了泛型類. 其實還可以定義一個帶有類型參數的簡單方法
public class ArrayAlg {
public static <T> T getMiddle(T... a) {
return a[a.length / 2];
}
}
這是一個泛型方法,從<>括號可以看出這一點,需要注意的是泛型方法類型變量放在修飾符後面(這裏是public static)返回值的前面.泛型方法可以定義在普通類,也可以定義在泛型類中.
調用一個泛型方法也非常簡單
String middle = ArrayAlg.getMiddle("zly","ppjun","public");
編譯器能夠根據傳入的String[]判斷出T一定是String.
類型變量的限定
有的時候我們需要對類型變量加以限定,比如我們想利用Compareble接口的compareTo方法去比較大小.那麼我們就需要傳入的參數實現了Compareble接口.這個時候就可以通過類型變量T設置限定符實現
public static <T extends Compareble> T min(T[] a)
現在,泛型的min方法就只能被實現了Compareble接口的類的數組調用
一個類型變量或者通配符可以有多個限定,例如
T extends Compareble & Serializeble
限定符用&分隔,類型變量用,分隔
由於java是單繼承多實現的,所以限定中可以有多個接口,但是最多隻有一個類,並且如果用一個類做限定那麼他必須放限定列表中第一個.
需要注意的是類型變量的限定只支持< T extends X >形式,不支持< T super X >形式不要跟後面介紹的通配符”?”混淆
泛型類型的繼承規則
在使用泛型的時候我們需要了解一些關於子類和繼承的規定,先舉一個例子Employee和Manager是繼承關係,而Pair< Employee >是Pair< Manager >父類麼?答案是”不是”.相信這和大多數人的想法不太一樣.
比如下面這個情況我們寫代碼時候應該都遇到過
public static void main(String[] args){
List<Manager> list = new ArrayList<>();
test(list);
}
public static void test(List<Employee> list){
}
test方法沒法調用,簡單的說無論T和S有什麼關係Pair< T >與Pair< S >沒有任何聯繫
泛型類可以擴展或者實現其他泛型類,這跟普通類沒有什麼區別,比如ArrayList< T >實現List< T >,一個ArrayList< Manager >可以被轉換爲List< Manager >,但是一個ArrayList< Manager >跟ArrayList< Employee >沒有關係
通配符類型
通配符的子類限定
Pair< ? extends Employee >
表示類型參數是Employee子類的Pair
像前面說的給test方法無法傳入List< Manager >的問題,解決方法很簡單:使用通配符
public static void test(List<? extends Employee> list)
List< Manager >是List< ? extends Employee >的子類
這裏有個問題需要注意當使用了List< ? extends Employee >後,list.add()方法將不能使用而list.get()是可以的,要了解原因看傳入< ? extends Employee >後的add和get方法.
boolean add(? extends Employee e)
? extends Employee get(int index)
編譯器只知道add方法參數是Employee的子類,但並不知道具體是什麼類型.add方法拒絕傳入任何特定的類型因爲?不能跟他匹配.
而get方法就不存在問題,我們將返回值賦給Employee完全合法.
通配符的超類限定
除了與類型變量十分相似extends限定,通配符還有一個附加能力,即指定一個超類限定符,如下
? super Manager
這個通配符限制爲Manager或者所有父類.可以爲方法提供參數,但不能使用返回值.例如List< ? super Manager >
boolean add(? super Manager e)
? super Manager get(int index)
編譯器不知道add方法確切類型,但是可以用Manager(或其子類)調用它,然而get方法因爲返回值不確定就只能用Object去接收他
總結下,帶有超類型限定(super)的通配符可以向泛型對象寫入,帶有子類型限定(extends)的通配符可以從泛型對象讀取
無限定通配符
還可以使用無限定的通配符,例如Pair< ? >,並且會有如下方法
? getFirst()
void setFirst(?)
getFirst方法返回值只能賦給object,而setFirst方法將不能被調用即使傳入Object也不行.那麼問題來了這個有什麼卵用.
看完下面這個栗子就知道啦
public static boolean isNull(Pair<?> pair){
return pair.getFirst() != null || pair.getSecond() != null;
}
沒錯就是這種判斷是否爲空的操作用?非常好使,因爲他不需要知道實際的類型.
總結
泛型類定義
public class Pair<T> {...}
泛型方法定義
public static <T> T getMiddle(T... a) {...}
泛型變量限定
<T extends X>
代表T只能爲X或者X的子類,不支持<T super X>
一個類型變量或者通配符可以有多個限定,
T extends Compareble & Serializeble
,但是最多隻有一個類,並且如果用一個類做限定那麼他必須放限定列表中第一個.
泛型類的繼承規則
- 無論T和S有什麼關係Pair< T >與Pair< S >沒有任何聯繫
通配符的子類限定
Pair<? extends Employee>
表示類型參數是Employee子類的Pair使用了List< ? extends Employee >後,list.add()方法將不能使用而list.get()是可以的.
通配符的超類限定
Pair<? super Manager>
這個通配符限制爲Manager或者所有父類,可以爲方法提供參數,但不能使用返回值.
預告
本篇到此結束,下一篇泛型(2)將講解泛型擦除和擦除所帶來的形形色色的問題敬請期待.