java編程之泛型(二)

第三部分 泛型接口以及泛型方法


1、接口泛型


接口使用泛型與類使用泛型沒有太大的區別

interface InterGenerics<T>{ T next();}

//實現接口InterGenerics,生成Fibonacci數列
 class Fibonacci implements InterGenerics<Integer>{
    private int count = 0;

    //將int型自動裝箱成它所對應的包裝類Integer
    public Integer next(){return fib(count++);}
    private int fib(int n){
        if(n<2) return 1;
        return fib(n-2) + fib(n-1);
    }
  }
通過上例我們可以看到java泛型的一個侷限性:基本類型是無法作爲類型參數的。不過java SE5及之後版本已經解決了這個問題,具有自動裝箱和拆箱(也稱打包和拆包)的功能。將基本類型int,double等裝箱成Integer,Double等包裝類,是自動裝箱過程,反之則是自動拆箱過程

2、泛型方法


在定義泛型方法時,只需將泛型參數列表置於返回類型之前

public class GenericMethod {
    public <T> void f(T t){
        System.out.println(t.getClass());
    }

    public static void main(String[] args){
        GenericsMethod gm = new GenericMethod();
        gm.f("test");
        gm.f(2.8);
        gm.f(2.8f);
        gm.f('q');
    }
}
泛型方法能夠讓該方法獨立於類而發生改變。在我們的代碼設計當中應當儘量使用泛型方法。
注意:對於一個static方法,它是無法訪問泛型類的類型參數,如果static需要使用泛型,就必須將它自身方法變爲泛型方法

這裏還有一點:我們在使用泛型類創建其對象時,必須指定類型參數的值,就像下面:

class Example<T>{
    public static void main(){
        Example<String> e = new Example<String>();
    }
}
而我們在使用泛型方法時不必指明參數,編譯器會自動找出具體類型,這個過程叫做:類型參數推斷(來自Java In Thinking)

可變參數與泛型方法

import java.util.*;

public class GenericVarargs {
    public static <T> List<T> getList(T...args) {
        List<T> container = new LinkedList<T>();
        for(T item: args)
            container.add(item);
        return container;
    }

    public static void main(String[] args){
        List<String> ls1 = getList("exmple");
        System.out.println(ls1);

        List<Character> ls2 = getList('a');
        System.out.println(ls2);
    }
}

第四部分 邊界


淺談擦除問題:

import java.util.*;

public class ErasedTypeEquivalence {
    public static void main(String[] args){
        List<String> c1 = new ArrayList<String>();
        List<Integer> c2 = new ArrayList<Integer>();
        System.out.println(c1.getClass()==c2.getClass());
    }
}/*output:
true
*/
上面代碼的輸出結果告訴我們在這個程序中`ArrayList<String>與ArrayList<Integer>`是相同的類型,這是爲什麼呢?
這是由於java泛型是由擦除來實現的,也就是當我們使用泛型時,任何具體的類信息都會被擦除。因此`List<String>和List<Integer>`在運行時實際是相同的類。這兩種類被擦除成它們的“原生”類型List.

再來看這樣一個例子

class HasF{
    public void f(){System.out.println("HasF.f()");}
}

class Operator<T> {
    private T obj;
    public Operator(T obj){this.obj = obj;}

    //operate方法中的obj試圖調用HasF類當中的f()
    //出現編譯器報錯:The method f() is undefined for the type T
    public void operate(){obj.f();}
}

public class Operation{
    public static void main(String[] String){
        HasF hf = new HasF();
        Operator<HasF> operator =  new Operator<HasF>(hf);
        operator.operate();
    }
}

造成以上錯誤原因是:由於有了擦除,雖然Operator在創建對象時將HasF對象最爲參數傳入,但是在Operator由於參數類型T是不確定的,所以它內部的operate方法卻無法通過java編譯器將自己必須在obj上調用f()這一需求映射到HasF擁有f()這一事實上。
於是我們引進邊界來對參數類型T做一個限定。限制T必須是HasF的派生類,這裏將重用extends關鍵字,修改後的代碼如下:

class HasF{
    public void f(){System.out.println("HasF.f()");}
}

//制定T的邊界HasF
class Operator<T extends HasF> {
    private T obj;
    public Operator(T obj){this.obj = obj;}
//此時調用f()編譯器不會報錯,代碼正常運行
    public void operate(){obj.f();}
}

public class Operation{
    public static void main(String[] String){
        HasF hf = new HasF();
        Operator<HasF> operator =  new Operator<HasF>(hf);
        operator.operate();
    }
}

以上就是邊界的概念。其實所謂邊界,簡單說就是爲泛型的參數類型做一個限定

發佈了32 篇原創文章 · 獲贊 51 · 訪問量 23萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章