Android開發之深入理解泛型extends和super的區別

來源:互聯網
上載者:User

標籤:android   寫代碼   boolean   eclips   總結   自動   應該   含義   儲存   

摘要:

什麼是泛型?什麼是擦除邊界?什麼是上界限定或下界限定(子類型限定或超類型限定)?什麼是型別安全?泛型extends關和super關鍵字結合萬用字元?使用的區別,兩種泛型在實際Android開發中有什麼用處?

一、什麼是泛型?

泛型,大概的意思是指沒有指定具體的類型的類或方法,以泛型的形式傳入一個類中或方法中,在Java編程(Android編程)裡面使用一對角括弧和一個大寫字母表示,例如:

//泛型類public interface List<E>{}//泛型方法,型別參數位於傳回型別之前或void之前public static <E> boolean contains(E [] arr, E x){   for(E val:arr){      if(x.equal(val)){         return true;      }   }   return false;}

角括弧中的內容常用一個大寫的字母表示,但也可以是任意的單詞或同時傳入多個泛型,它的命名規則遵循類名的命名規則(首字母大寫),例如:

public interface List<AnyType,A,B,C>{}

在一個聲明了傳入泛型的類中,我們可以考慮傳入,也可以考慮不傳入,比如,現在我們需要一個列表存放資料(但沒有明確資料的類型),那麼執行個體化的列表,就可以存放各種類型的資料,代碼如下:

//沒有具體類型的列表ArrayList<> mDataSet=new ArrayList<>();         mDataSet.add(new Object());        mDataSet.add(new String());        mDataSet.add(new Integer(0));        mDataSet.add(new Animal());//指定具體類型的列表(儲存其他類型,編譯不通過,編譯器報錯)ArrayList<String> mDataSetStr=new ArrayList<>();        mDataSetStr.add(new String());

為了更好理解萬用字元的用法,現在我們先定幾個繼承關係的類:WhiteCuteHelloKittyCuteHelloKittyHelloKittyCatAnimal,如:

萬用字元?既可以單獨使用,也可以結合Java關鍵字extendssuper一起使用,關於它們之間的區別在後面介紹,例如:

  //萬用字元單獨使用ArrayList<?> mDataSetType=new ArrayList<>();  //萬用字元結合關鍵字extends一起使用ArrayList<? extends Animal> mAnimalChildren = new ArrayList<>();  //萬用字元結合關鍵字super一起使用ArrayList<? super HelloKitty> mAnimalParents = new ArrayList<>();

這裡,沒有列舉完泛型的用法,關於其他的一些用法可以參考其他文檔,繼續我們下面的介紹。

二、 什麼是擦除邊界?

擦除邊界,指一個具體的類型,抽象成一個泛型,那麼我們就可以在編寫代碼的時候,根據實際需要指定具體的類型,符合依賴抽象的編程思想。我們使用IDE或Eclipse開發工具編寫代碼時,在編譯期會將傳入的具體類型代替泛型,同時方便使用具體類型的屬性和方法,比如,我們在一個列表中指定儲存Animal這個具體類,那麼我們就可以方便使用Animal內部的屬性和方法,例如:

    class Animal {        String name;        public void setName(String name) {            this.name = name;        }        public String getName() {            return name;        }    }ArrayList<Animal> mAnimals = new ArrayList<>();//指定ArrayList的泛型具體類為Animal,其對象可以儲存Animal及其子類的對象        mAnimals.add(new Animal());        mAnimals.add(new Cat());        mAnimals.add(new HelloKitty());//方便使用Animal內部的屬性和方法        String name=mAnimals.get(0).name;               name=mAnimals.get(1).getName();

簡單地說,擦除邊界就像一個正方形去掉了四邊,變成一個沒有固定大小沒有邊界的圖片,在以後需要使用的時候,再給它指定具體的類型,指定的類型可以是圓形、矩形、三角形或六邊形、心形等。

三、什麼是上界限定或下界限定?

在Java程式中,類與類之間最重要的一種關係——繼承關係,簡單的描述是一個縱向的排列關係(如上面Animal的UML圖)。上界,指的是往上能夠找到的最頂端的類(忽略Object類);下界,指的是外下能夠找到的最末端的類。泛型<? extends HelloKitty>表示的是所有繼承自HelloKitty的子類,那麼萬用字元表示的上界是HelloKitty,但我們不知道它的下界;泛型<? super CuteHelloKitty>表示的是所有CuteHelloKitty的超類,萬用字元?表示的下界是CuteHelloKitty,同理我們不知道它的上界。於是,初學者在看一些介紹extendssuper區別的文章時,容易將extends的上界和super的下界混到一起記憶,實際上它們彼此擁有自己的上界與下界,這是特別需要注意的!

四、什麼是型別安全?

型別安全,指的是在編碼階段,編譯器自動對代碼進行檢查,檢查變數或方法的調用是否符合當前類型,如果有不符合的情況,Java編譯器就會提示錯誤,比如,一個HelloKitty的對象mHelloKitty允許調用自身聲明或繼承的屬性和方法,這是符合型別安全的;但是,一個HelloKitty的對象mHelloKitty不允許調用子類CuteHelloKittyWhiteCuteHelloKitty聲明的屬性和方法,如果強行調用了,編譯器會提示錯誤,這是不符合型別安全的。

簡單地說,上溯造型是符合型別安全的,下溯造型是不符合型別安全的。

五、 萬用字元 ?與extends、super關鍵字使用的區別

從上面的學習中,我們知道<? extends T>表示的含義是繼承自T的一組類,在一個泛型類List<E>(或方法)中傳入泛型<? extends T>,編譯器會自動轉換成如下代碼:

//泛型類public interface List<? extends T>{    //泛型方法    boolean add(? extends T e);}

下面示範的例子,釗林將T用具體的類Animal代替,在一個泛型類ArrayList<E>傳入<? extends Animal>,然後嘗試往mAnimalChildren列表中存入子類的對象,最後查看ArrayList的add方法,如:

你會發現,編譯器報錯了,不允許往一個列表中儲存Animal本身及其子類的對象,難道我理解錯誤了嗎?這到底是為什嗎?

在開發人員的頭腦裡,自然很容易理解AnimalCat是符合泛型<? extends Animal>規則的,但是對於編譯器來說,它只知道上面泛型表示的是一組類,但不清楚是否是具體的Animal類或其子類,在ArrayList類中,傳入的泛型,編譯器會自動將類中的泛型替換成<? extends Animal>,比如add方法,變成了boolean add(? extends Animal e),這時候試圖傳入具體的類AnimalCatHelloKittyCuteHelloKitty,編譯器會很生氣地告訴你說:“你是不是聽不懂我說的話,我要求的是? extends Animal,你卻給我一個Animal或其子類!!產生了疑惑:我應該把它當成Animal處理呢?還是應該當成HelloKitty處理呢?抑或是當成CuteHelloKitty處理呢?”因為存在類型不清楚的情況,所以編譯器禁止開發人員傳入具體的類型,

這就是為什麼釗林會花一些篇幅提前介紹什麼是型別安全的原因,對於編譯器要求的是一個A類型,開發人員給了一個B類型,這是不符合型別安全的!

? extends Animal表示一種類型,AnimalCatHelloKitty表示另一種類型,所以會報錯!

既然不允許我們往裡面添加資料,那麼,對於泛型? extends Animal,對於開發人員到底有什麼用處?雖然編譯器不允許你往add方法傳入資料。

但是對於編譯器來說泛型類? extends Animal,可以肯定其繼承了Animal的屬性和方法,那麼在我們封裝類的時候,就可以方便地調用繼承的屬性和方法,例如:

    class Animal {        String name;        public void setName(String name) {            this.name = name;        }        public String getName() {            return name;        }    }    class CuteHelloKitty extends HelloKitty{        public String feature(){            return "This is a cute HelloKitty !";        }    }    /**     * 封裝的一個類     * @param <T>     */    class AnimalName<T>{        T e;        public T get() {            return e;        }        public T print(T e) {            return e;        }    }    public void print(){        //一、綁定為一群組繼承自Animal的類,允許使用繼承的屬性和方法        AnimalName<? extends Animal> animalName=new AnimalName();        Animal animal=animalName.get();        System.out.print(animal.getName());        //二、綁定為一組CuteHelloKitty的類,允許使用繼承的屬性和方法        AnimalName<? extends CuteHelloKitty> cuteHelloKittyName=new AnimalName();        CuteHelloKitty cuteHelloKitty=cuteHelloKittyName.get();        System.out.print(cuteHelloKitty.feature());    }

既然泛型<? extends Animal>不合適往裡面添加資料,那麼開發人員在設計程式的時候,對於傳入泛型<? extends Animal>的類,盡量避免調用往寫入資料的方法,只調用讀取資料的方法。泛型類<? extends Animal>表示以Animal為上界的所有子類,不確定具體的類型,可能會下溯造型,不符合型別安全!!

但是,如果我想要調用寫入資料的方法呢,那該怎麼辦?那麼,你可以考慮使用泛型<? super T>,該泛型表示的含義是以T為下界的一組類,如:

看一下下面的例子,編譯器允許開發人員這樣子操作,代碼如下:

        //三、綁定為以CuteHelloKitty為下界的一組類        AnimalName<? super CuteHelloKitty> helloKitty = new AnimalName<>();        helloKitty.print(new CuteHelloKitty());        helloKitty.print(new WhiteCuteHelloKitty());

類中傳入泛型<? super T>,對於傳入的CuteHelloKitty類本身及其子類,在這裡編譯器會自動上溯造型為CuteHelloKitty,上溯造型符合型別安全的,因此可以調用寫入資料的print方法)。編譯器允許傳入的是T自身或T子類的對象,最終上溯造型為T,符合型別安全!!

總結:

在一個封裝類中傳入泛型,可以在編碼的時候指定泛型為某個具體的類,也可以指定為一組類,指定為一組類可以考慮使用萬用字元?extendssuper關鍵字結合,泛型類<? extends T>表示以T為上界的一組子類,適合讀取資料,不建議調用寫入資料的方法,否則編譯器會報錯;泛型<? super T>表示以T為下界的一組超類,適合調用寫入資料的方法,不適合讀取資料。

在開發中,根據實際的需要合理選擇傳入<? extends T><? super T>或者不使用萬用字元,符合型別安全!!

Android開發之深入理解泛型extends和super的區別

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.