java的泛型

泛型,就是參數(shù)化類型的意思,具體表現(xiàn)為泛型類,泛型接口,泛型方法。

泛型主要用于編譯過程不確定參數(shù)可能的類型,需要對參數(shù)進(jìn)行統(tǒng)一邏輯處理,比較常用在設(shè)計(jì)模式里。

泛型只在編譯階段有作用,編譯器檢查泛型結(jié)果無誤后會擦去泛型相關(guān)信息,并且在對象進(jìn)入和離開的邊界加入類型檢查和類型轉(zhuǎn)換的方法。泛型不會進(jìn)入到運(yùn)行時(shí)階段。

public class TestFanxing {
    public static void main(String[] args) {
        test1();
    }
    public static void test1() {
        // 泛型只在編譯時(shí)有作用,編譯器檢查完泛型類型后,會擦去泛型信息,只在對象進(jìn)入和離開的邊界出加入類型檢查和轉(zhuǎn)換的方法。
        // 運(yùn)行時(shí)不存在泛型概念。
        List<String> stringArrayList = new ArrayList<String>();
        List<Integer> integerArrayList = new ArrayList<Integer>();

        Class classStringArrayList = stringArrayList.getClass();
        Class classIntegerArrayList = integerArrayList.getClass();

        if(classStringArrayList.equals(classIntegerArrayList)){
            System.out.println("泛型測試,泛型相同");
        }
    }
}

結(jié)果:

泛型測試,泛型相同

泛型類
定義類的時(shí)候使用了泛型,該類就是泛型類。常見使用的有List,Set,Map。
一個(gè)基本的泛型類:

public class Generic<T>{ 
    //key這個(gè)成員變量的類型為T,T的類型由外部指定,代表泛型的字母可以隨便寫
    private T key;

    public Generic(T key) { //泛型構(gòu)造方法形參key的類型也為T,T的類型由外部指定
        this.key = key;
    }

    public T getKey(){ //泛型方法getKey的返回值類型為T,T的類型由外部指定
        return key;
    }
}

泛型的類型參數(shù)只能是類類型,不能是簡單類型。當(dāng)然也可以不傳,默認(rèn)可以是任意類型。

public static void test2() {
        class Generic<T> {
            private T key;

            public Generic(T key) {
                this.key = key;
            }
            public T getKey() {
                return key;
            }
        }

        //泛型的類型參數(shù)只能是類類型(包括自定義類),不能是簡單類型
        //傳入的實(shí)參類型需與泛型的類型參數(shù)類型相同,即為Integer.
        Generic<Integer> genericInteger = new Generic<Integer>(123456);

        //傳入的實(shí)參類型需與泛型的類型參數(shù)類型相同,即為String.
        Generic<String> genericString = new Generic<String>("key_vlaue");

        System.out.println("泛型測試" + "key is " + genericInteger.getKey());
        System.out.println("泛型測試" + "key is " + genericString.getKey());

        // 如果定義時(shí)不傳入泛型實(shí)參,則默認(rèn)可以是任意類型
        Generic generic = new Generic("111111");
        Generic generic1 = new Generic(4444);
        Generic generic2 = new Generic(55.55);
        Generic generic3 = new Generic(false);

        System.out.println("泛型測試" + "key is " + generic.getKey());
        System.out.println("泛型測試" + "key is " + generic1.getKey());
        System.out.println("泛型測試" + "key is " + generic2.getKey());
        System.out.println("泛型測試" + "key is " + generic3.getKey());
    }

結(jié)果:

泛型測試key is 123456
泛型測試key is key_vlaue
泛型測試key is 111111
泛型測試key is 4444
泛型測試key is 55.55
泛型測試key is false

不能對確切的泛型類型使用instanceof操作。編譯時(shí)會出錯(cuò)。

泛型接口
泛型接口的使用與泛型類級別相同

//定義一個(gè)泛型接口
public interface Generator<T> {
public T next();
}

實(shí)現(xiàn)泛型接口的時(shí)候,定義要加上泛型聲明,否則會報(bào)錯(cuò)。

 /**
 * 未傳入泛型實(shí)參時(shí),與泛型類的定義相同,在聲明類的時(shí)候,需將泛型的聲明也一起加到類中。
 * 即:class FruitGenerator<T> implements Generator<T>{
 * 如果不聲明泛型,如:class FruitGenerator implements Generator<T>,編譯器會報(bào)錯(cuò):"Unknown class"
 */
class FruitGenerator<T> implements Generator<T>{
    @Override
    public T next() {
        return null;
    }
}
/**
 * 傳入泛型實(shí)參時(shí):
 * 定義一個(gè)生產(chǎn)器實(shí)現(xiàn)這個(gè)接口,雖然我們只創(chuàng)建了一個(gè)泛型接口Generator<T>
 * 但是我們可以為T傳入無數(shù)個(gè)實(shí)參,形成無數(shù)種類型的Generator接口。
 * 在實(shí)現(xiàn)類實(shí)現(xiàn)泛型接口時(shí),如已將泛型類型傳入實(shí)參類型,則所有使用泛型的地方都要替換成傳入的實(shí)參類型
 * 即:Generator<T>,public T next();中的的T都要替換成傳入的String類型。
 */
public class FruitGenerator implements Generator<String> {

    private String[] fruits = new String[]{"Apple", "Banana", "Pear"};

    @Override
    public String next() {
        Random rand = new Random();
        return fruits[rand.nextInt(3)];
    }
}

泛型通配符
泛型通配符簡單看就是一個(gè)“?”,但是千萬不要跟泛型形參搞混,它是泛型實(shí)參!相當(dāng)于我們傳入了實(shí)際的Number,Integer等,可以把“?”看成所有類型的父類,是一種真實(shí)的類型。
這個(gè)通配符解決的問題也很明顯,就是使用泛型實(shí)參的時(shí)候,如果不確定可能的類型,就用這個(gè)。

泛型方法
泛型方法的聲明基本是下面這個(gè)樣子:

public <E> void show_3(E t){ // <E>是必須的,否則就是普通的方法
System.out.println(t.toString());
}

普通類中的泛型方法
使用要注意,<>中的符號不是隨便寫的,應(yīng)該是可之前定義過的泛型形參,仔細(xì)看下面的例子來幫助理解。

public class GenericTest {
  public class Generic<T>{     
        private T key;

        public Generic(T key) {
            this.key = key;
        }

        //我想說的其實(shí)是這個(gè),雖然在方法中使用了泛型,但是這并不是一個(gè)泛型方法。
        //這只是類中一個(gè)普通的成員方法,只不過他的返回值是在聲明泛型類已經(jīng)聲明過的泛型。
        //所以在這個(gè)方法中才可以繼續(xù)使用 T 這個(gè)泛型。
        public T getKey(){
            return key;
        }

        /**
         * 這個(gè)方法顯然是有問題的,在編譯器會給我們提示這樣的錯(cuò)誤信息"cannot reslove symbol E"
         * 因?yàn)樵陬惖穆暶髦胁⑽绰暶鞣盒虴,所以在使用E做形參和返回值類型時(shí),編譯器會無法識別。
        public E setKey(E key){
             this.key = keu
        }
        */
    }

//這也不是一個(gè)泛型方法,這就是一個(gè)普通的方法,只是使用了Generic<Number>這個(gè)泛型類做形參而已。
    public void showKeyValue1(Generic<Number> obj){
        Log.d("泛型測試","key value is " + obj.getKey());
    }

    //這也不是一個(gè)泛型方法,這也是一個(gè)普通的方法,只不過使用了泛型通配符?
    //同時(shí)這也印證了泛型通配符章節(jié)所描述的,?是一種類型實(shí)參,可以看做為Number等所有類的父類
    public void showKeyValue2(Generic<?> obj){
        Log.d("泛型測試","key value is " + obj.getKey());
    }

     /**
     * 這個(gè)方法是有問題的,編譯器會為我們提示錯(cuò)誤信息:"UnKnown class 'E' "
     * 雖然我們聲明了<T>,也表明了這是一個(gè)可以處理泛型的類型的泛型方法。
     * 但是只聲明了泛型類型T,并未聲明泛型類型E,因此編譯器并不知道該如何處理E這個(gè)類型。
    public <T> T showKeyName(Generic<E> container){
        ...
    }  
    */

    /**
     * 這個(gè)方法也是有問題的,編譯器會為我們提示錯(cuò)誤信息:"UnKnown class 'T' "
     * 對于編譯器來說T這個(gè)類型并未項(xiàng)目中聲明過,因此編譯也不知道該如何編譯這個(gè)類。
     * 所以這也不是一個(gè)正確的泛型方法聲明。
    public void showkey(T genericObj){

    }
    */

 /**
  正確的普通類中的泛型方法
 */
  public <T> T showKeyName(Generic<T> container){
        System.out.println("container key :" + container.getKey());
        //當(dāng)然這個(gè)例子舉的不太合適,只是為了說明泛型方法的特性。
        T test = container.getKey();
        return test;
    }
}

泛型類中的泛型方法

    public void show_1(T t){
            System.out.println(t.toString());
        }

        //在泛型類中聲明了一個(gè)泛型方法,使用泛型E,這種泛型E可以為任意類型??梢灶愋团cT相同,也可以不同。
        //由于泛型方法在聲明的時(shí)候會聲明泛型<E>,因此即使在泛型類中并未聲明泛型,編譯器也能夠正確識別泛型方法中識別的泛型。
        public <E> void show_3(E t){
            System.out.println(t.toString());
        }

        //在泛型類中聲明了一個(gè)泛型方法,使用泛型T,注意這個(gè)T是一種全新的類型,可以與泛型類中聲明的T不是同一種類型。
        public <T> void show_2(T t){
            System.out.println(t.toString());
        }
    }

簡單來說

沒在“<>”中聲明過就不要直接用,編譯器不認(rèn)識。

靜態(tài)方法與泛型
靜態(tài)方法無法訪問到類定義的泛型。
如果靜態(tài)方法要使用泛型,就要額外用<>聲明

public class StaticGenerator<T> {
    /**
     * 如果在類中定義使用泛型的靜態(tài)方法,需要添加額外的泛型聲明(將這個(gè)方法定義成泛型方法)
     * 即使靜態(tài)方法要使用泛型類中已經(jīng)聲明過的泛型也不可以。
     * 如:public static void show(T t){..},此時(shí)編譯器會提示錯(cuò)誤信息:
          "StaticGenerator cannot be refrenced from static context"
     */
    public static <T> void show(T t){

    }
}

泛型邊界
指的是泛型也可以有繼承關(guān)系,用來限制形參的范圍。

泛型通配符設(shè)置邊界

public void showKeyValue1(Generic<? extends Number> obj){
    Log.d("泛型測試","key value is " + obj.getKey());
}

Generic<String> generic1 = new Generic<String>("11111");
//這一行代碼編譯器會提示錯(cuò)誤,因?yàn)镾tring類型并不是Number類型的子類showKeyValue1(generic1);

泛型類定義的時(shí)候也可以設(shè)置邊界

public class Generic<T extends Number>{
    private T key;

    public Generic(T key) {
        this.key = key;
    }

    public T getKey(){
        return key;
    }
}

泛型方法設(shè)置邊界

//在泛型方法中添加上下邊界限制的時(shí)候,必須在權(quán)限聲明與返回值之間的<T>上添加上下邊界,即在泛型聲明的時(shí)候添加
//public <T> T showKeyName(Generic<T extends Number> container),編譯器會報(bào)錯(cuò):"Unexpected bound"
public <T extends Number> T showKeyName(Generic<T> container){
    System.out.println("container key :" + container.getKey());
    T test = container.getKey();
    return test;
}

關(guān)于泛型數(shù)組
在java中是不能創(chuàng)建一個(gè)確切的泛型類型的數(shù)組的。只能用默認(rèn)類型或者泛型通配符。

List<String>[] ls = new ArrayList<String>[10]; // 不被允許
List<?>[] ls = new ArrayList<?>[10]; // 允許
List<String>[] ls = new ArrayList[10]; // 允許

//        List<String> list0 = new ArrayList<String>[3]; // java不允許創(chuàng)建明確泛型類型的數(shù)組
        List<?>[] list = new ArrayList[3]; // 創(chuàng)建了一個(gè)List類型的數(shù)組,List是個(gè)泛型類,即創(chuàng)建了一個(gè)泛型的數(shù)組
        ArrayList<Integer> value = new ArrayList<Integer>(); // 準(zhǔn)備一個(gè)數(shù)組元素
        value.add(5);
        list[0] = value; // 賦值
        Integer r = (Integer) list[0].get(0); // 如果定義數(shù)組沒有使用通配符?而是明確的Integer,這里就不需要強(qiáng)轉(zhuǎn)

參考文章:
java 泛型詳解

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容