JAVA泛型詳解(上)

*1.泛型定義*
泛型是在定義接口,類時指定類型形參,這個類型形參將在創建對象,聲明變量時明確指定。JDK5集合框架中的大量使用了泛型,所以在使用集合框架時可以明確指定具體的類型參數。下圖聲明瞭一個類使用泛型:

public abstract class PhoneFactory<T> {

    private T factory;


    public abstract Phone productPhone(T t);

    public T getFactory() {
        return factory;
    }

    public void setFactory(T factory) {
        this.factory = factory;
    }

}

2.從泛型派生出子類
如果父類或接口定義時採用了泛型,則子類繼承或實現接口時,父類或接口中聲明的泛型參數必須指明具體的類型或不指定。
(1):如果父類或者接口中方法參數使用參數,則子類在實現父類或接口時,必須明確指定具體的類型,否則編譯報錯:
a.父類代碼如下:

public abstract class PhoneFactory<T> {

    private T factory;


    public abstract Phone productPhone(T t);

    public T getFactory() {
        return factory;
    }

    public void setFactory(T factory) {
        this.factory = factory;
    }

}
b.子類代碼如下:
/**
 * 子類實現擴展帶有泛型的父類時,此時父類不能包含形參類似 **PhoneFactory<T>,
 * 必須指明一個具體的類型
 *
 */
public class AppleFactory extends PhoneFactory<Phone> {

    @Override
    public Phone productPhone(Phone phone) {    
        return new ApplePhone();
    }
}

(2).如果父類或接口中的方法中沒有使用泛型,則定義子類時可以不指定具體的類型,JDK編譯警告:

public interface CarProductService<T> {

    public void productCar(String carType);
}


public class BenzCarProductService implements CarProductService {

    @Override
    public void productCar(String carType) {
        // TODO Auto-generated method stub

    }

}

3.泛型注意事項
(1).靜態變量不能使用泛型,靜態變量屬於類級別不屬於某個實例的。
(2).並不存在具體的泛型類,這個可能不是很好理解,通過例子很容易看明白,例如:ArrayList 與ArrayList屬於同一個類,JVM編譯之後不會生成兩個class文件。
這裏寫圖片描述
a.通過程序實驗結果可以看出不存在具體的泛型類。
4.類型通配符上限
(1).對於已經定義了泛型的類或接口,在使用帶有泛型的類或接口(創建對象或聲明變量)時,如果沒有傳入具體的類型參數,會引發編譯警告。大多數情況下,對於定義的方法或類中的參數類型不確定,這個時候使用類型通配符,類型通配符就是一個問號 (? )
a.請看如下代碼,定義了一個方法遍歷集合元素,List中已經使用泛型,程序中沒有使用,引起編譯警告:
這裏寫圖片描述
b.上述程序不是很好的寫法,請看下面使用List:

public void test(List<Object> list){
        for(int i=0;i<list.size();i++){
            System.out.println(list.get(i));
        }
    }
b1.將程序修改爲List<Object>是不會引起編譯警告,但是在實際調用時可能不是我們所期望的。比如,我們在調用時傳入List<String>,則會發生編譯錯誤:

這裏寫圖片描述
b2.上述程序反應一個問題,List並不是List的子類。在JAVA中,如果T中使用了泛型,A是B的一個子類,則T《A》並不是T《B》的子類型。
(2).將上述方法進一步改進,可以在方法參數上修改爲List<?>,這樣在調用test方法時,無論傳入List<String>還是List<Integer>都不會發生錯誤
這裏寫圖片描述
(3).在定義接口或類時,可以使用泛型,但是不能使用通配符,只有在使用定義了泛型的類或接口時,纔可以使用通配符?
5.類型通配符上限
1.再說類型通配符之前,先看學習一下泛型的繼承。在JAVA中如果父類T有兩個子類T1,T2,則類A<T>不是類A<T1>和類A<T2>的父類。沒有繼承關係。
這裏寫圖片描述
2.上述圖中,可以用一個具體例子解釋一下:
2.1定義三個類,Phone,HTCPhone,ApplePhone。Phone是父類。其他兩個爲子類。
這裏寫圖片描述
2.2 定義生產Phone的服務,採用泛型方式聲明。如下
這裏寫圖片描述
2.2.1 具體實現代碼如下:

public interface PhoneSaleService<T> {


    public void salePhoneService(PhoneSaleService<T> sale);


}

2.2.2 實現類如下:

public class HtcPhoneSaleService<T> implements PhoneSaleService<T> {

    @Override
    public void salePhoneService(PhoneSaleService<T> sale) {
            System.out.println("*******採購手機,下訂單******");
            System.out.println("****進貨**");
            System.out.println("****登記入庫**");
    }
}

2.2.3 : 測試類:
這裏寫圖片描述
通過上述案例可以看出,PhoneSaleService<Phone>並不是PhoneSaleService<HTCPhone>的父類,引起編譯錯誤。這個在泛型中應該特別注意。
3.爲解決上述問題,可以採用通配符的方式來解決。JAVA中對於泛型還採用了類型限制,也是通過通配符來實現。

public interface PhoneSaleService<T extends Phone > {

    public void salePhoneService(PhoneSaleService<? extends Phone> sale);

}

實現類:

public class HtcPhoneSaleService<T extends Phone> implements PhoneSaleService<Phone> {

    @Override
    public void salePhoneService(PhoneSaleService<? extends Phone> sale) {
            System.out.println("*******採購手機,下訂單******");
            System.out.println("****進貨**");
            System.out.println("****登記入庫**");
    }
}

測試類,就不會報上述編譯錯誤:
這裏寫圖片描述
5.類型通配符下限

6.泛型方法

6.1 泛型方法形式如下:修飾符 <T,S> 方法返回類型 方法名(泛型參數列表)。
6.2 泛型方法與普通方法區別就是在修飾符後面,方法返回類型前面用<>使用了修飾符。其中,方法參數列表中也使用了泛型參數。
6.3泛型方法主要闡明瞭方法中一個參數或者多個參數之間的依賴的關係,或者方法返回值與方法參數之間的依賴關係。沒有依賴關係,則沒有必要使用泛型方法。
6.4 泛型方法必須要有修飾符。

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