泛型(一)->簡單使用

泛型(一)->簡單使用

從上週末到現在陸陸續續看了幾天終於把<< 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;
 }

沒錯就是這種判斷是否爲空的操作用?非常好使,因爲他不需要知道實際的類型.

總結

  1. 泛型類定義

    • public class Pair<T> {...}
  2. 泛型方法定義

    • public static <T> T getMiddle(T... a) {...}
  3. 泛型變量限定

    • <T extends X>代表T只能爲X或者X的子類,不支持<T super X>

    • 一個類型變量或者通配符可以有多個限定,T extends Compareble & Serializeble,但是最多隻有一個類,並且如果用一個類做限定那麼他必須放限定列表中第一個.

  4. 泛型類的繼承規則

    • 無論T和S有什麼關係Pair< T >與Pair< S >沒有任何聯繫
  5. 通配符的子類限定

    • Pair<? extends Employee> 表示類型參數是Employee子類的Pair

    • 使用了List< ? extends Employee >後,list.add()方法將不能使用而list.get()是可以的.

  6. 通配符的超類限定

    • Pair<? super Manager> 這個通配符限制爲Manager或者所有父類,可以爲方法提供參數,但不能使用返回值.

預告

本篇到此結束,下一篇泛型(2)將講解泛型擦除和擦除所帶來的形形色色的問題敬請期待.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章