java編程思想讀書筆記 第十五章 泛型 (泛型方法)

泛型方法
首先需要知道的是,可以在類中包含參數化方法,而這個方法所在的類可以是泛型類,也可以不是泛型類。也就是說,是否擁有泛型方法,與其所在的類是否是泛型沒有關係。泛型方法使得該方法能夠獨立於類而產生變化。一個基本原則是:無論何時,只要你能做到,你就應該儘量使用泛型方法。也就是說,如果使用泛型方法可以取代將整個類泛型化,那麼就應該只使用泛型方法,因爲它可以使事情更清楚明白。另外,對於一個 static 的方法而言,無法訪問泛型類的類型參數,所以,如果 static 方法需要使用泛型能力,就必須使其成爲泛型方法。
要定義泛型方法,只需將泛型參數列表置於返回值之前,比如:public void f(T x){};

1)槓桿利用類型參數推斷
在泛型方法中,類型參數推斷可以簡化一部分工作。例如,編寫一個工具類,它包含各種各樣的static方法,專門用來創建各種常用的容器對象,例如:

public class New {
    public static <K,V> Map<K,V> map() {
        return new HashMap<K, V>();
    }
    public static <T> List<T> list() {
        return new ArrayList<T>();
    }
    public static <T> LinkedList<T> lList(){
        return new LinkedList<T>();
    }
    public static <T> Set<T> set(){
        return new HashSet<T>();
    }
    public static <T> Queue<T> queue(){
        return new LinkedList<T>();
    }
    public static void main(String[] args) {
        Map<String, List<String>> sls = New.map();
        List<String> ls = New.lList();
        Set<String> ss = New.set();
        Queue<String> qs = New.queue();
    }
}

main()方法演示瞭如何使用這個工具類,類型參數推斷避免了重複的泛型參數列表。類型推斷只對複製操作有效,其他時候並不起作用。如果你將一個泛型方法調用的結果作爲參數,傳遞給另一個方法,這時編譯器並不會執行類型推斷。
顯示的類型說明,在泛型方法中,可以顯示地指明類型,不過這種語法很少用。要顯示地指明類型,必須在點操作符之前使用this關鍵字,如果是使用static的方法,必須在點操作符之前加上類名。例如New.<Person,List<Pet>> map();

2)可變參數與泛型方法
泛型方法與可變參數列表能夠很好地共存,例如:

public class GenericVarargs {
    public static <T> List<T> makeListT(T... args){
        List<T> reList = new ArrayList<T>();
        for (T item : args) {
            reList.add(item);
        }
        return reList;
    }
    public static void main(String[] args) {
        List<String> ls = makeListT("A");
        System.out.println(ls);
        ls = makeListT("A","B","C");
        System.out.println(ls);
        ls = makeListT("ABCDEFHIJKLMNOPQRSTUVWXYZ".split(""));
        System.out.println(ls);
    }

}

makeListT()方法展示了與標準類庫中java.util.ArrayList()方法相同的功能。

3)用於Generator的泛型方法
利用生成器,可以很方便地填充一個Collection,而泛型化這種操作時具有實際意義的,例如:

public class Generators {
    public static <T> Collection<T> fill(Collection<T> coll,Generator<T> gen,int n) {
        for (int i = 0; i < n; i++) {
            coll.add(gen.next());
        }
        return coll;
    }
    public static void main(String[] args) {
        Collection<Coffee> coffees = fill(new ArrayList<Coffee>(), new CoffeeGenerator(), 4);
        for (Coffee coffee : coffees) {
            System.out.println(coffee);
        }       
    }
}

其中Generator接口、Coffee類以及CoffeeGenerator類在上一篇博客中有,在這個例子中,fill()方法使如何透明地應用於Coffee的容器。

4)一個通用的Generator
下面的程序可以爲任何類構造一個Generator,只要該類具有默認的構造器。爲了減少類型聲明,它提供了一個泛型方法,用以生成BasicGenerator:

public class BasicGenerator<T> implements Generator<T>{
    private Class<T> type;
    public BasicGenerator(Class<T> type){
        this.type = type;
    }
    @Override
    public T next() {
        try {
            return type.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    public static <T> Generator<T> create(Class<T> type) {
        return new BasicGenerator<T>(type);
    }
}

這個類提供了一個基本實現,用以生成某個類的對象。這個類必須具備兩個特點:(1)它必須聲明爲public。(因爲BasicGenerator與要處理的類在不同的包中,所以該類必須聲明爲public,並且不只具有包內訪問權限。)(2)它必須具備默認的構造器(無參的構造器)。要創建這樣的BasicGenerator對象,只需調用create()方法,並傳入想要生成的類型。泛型化的create()方法允許執行BasicGenerator.create(MyType.class),而不必執行麻煩的new BasicGenerator(MyType.class)。
例如,下面是一個具有默認構造器的簡單的類:

public class CountObject {
    private static long counter = 0;
    private final long id = counter ++;
    public long id(){
        return id;
    }
    public String toString(){
        return "CountObject" + id;
    }
}

CountObject類能夠記錄下它創建了多少個CountObject實例,並通過toString()方法告訴我們其編號。
使用BasicGenerator,你可以很容易地位CountObject創建一個Generator:

public class BasicGeneratorDemo {
    public static void main(String[] args) {
        Generator<CountObject> generator = BasicGenerator.create(CountObject.class);
        for (int i = 0; i < 5; i++) {
            System.out.println(generator.next());
        }
    }
}
output:
CountObject0
CountObject1
CountObject2
CountObject3
CountObject4

可以看到,使用泛型方法創建Generator對象,大大減少了我們要編寫的代碼。java泛型要求傳入Class對象,以便也可以在create()方法中用它進行類型推斷。
5)一個Set的實用工具
作爲泛型方法的另一個示例,通過使用泛型方法,可以很方便地做到使用Set來表達數學中的關係式,而且可以應用於多種類型:

public class Sets {
     public static <T> Set<T> union(Set<T> a,Set<T> b){
         Set<T> result = new HashSet<T>(a);
         result.addAll(b);
         return result;
     }
     public static <T> Set<T> intert(Set<T> a,Set<T> b){
         Set<T> result = new HashSet<T>(a);
         result.retainAll(b);
         return result;
     }
     public static <T> Set<T> difference(Set<T> superset,Set<T> subset){
         Set<T> result = new HashSet<T>(subset);
         result.removeAll(subset);
         return result;
     }
     public static <T> Set<T> complement(Set<T> a,Set<T> b){
         return difference(union(a, b), intert(a, b));
     }
}

在前三個方法中,都將第一個參數Set複製了一份,將Set中的所有引用都存入一個新的HashSet對象中,因此,我們並未直接修改參數中的Set。返回值是一個全新的Set對象。這四個方法表達了一個數學集合的操作,union()返回一個Set,它將兩個參數合併在一起,intert()返回的Set只包含兩個參數共有的部分;difference()方法從superset中移除subset包含的元素;complement()返回的Set包含除了交集之外的所有元素。

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