<kbd id="5sdj3"></kbd>
<th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>

    一文通關苦澀難懂的Java泛型

    共 27499字,需瀏覽 55分鐘

     ·

    2021-04-08 10:05

    前言

    相信大家對Java泛型并不陌生,無論是開源框架還是JDK源碼都能看到它,毫不夸張的說,泛型是通用設計上必不可少的元素,所以真正理解與正確使用泛型,是一門必修課,本文將解開大家對泛型的疑惑,并通過大量實踐,讓你get到泛型正確的使用姿勢,下面開始進入正題吧!

    大綱

    基礎

    因為本文重實踐,而且面對的是Java開發(fā)人員群體,大家對泛型都有基礎,所以泛型基礎這塊會快速過,幫助大家回憶下即可,后面主要的則重點是通配符

    編譯期與運行期

    編譯期是指把源碼交給編譯器編譯成計算機可執(zhí)行文件的過程,運行期是指把編譯后的文件交給計算機執(zhí)行,直到程序結束。

    Java中就是把.java文件編譯成.class文件,再把編譯后的文件交給J V M加載執(zhí)行,如下圖

    泛型

    泛型又叫“參數(shù)化類型”,這么抽象的專業(yè)詞匯不好理解,阿星就用大白話的形式來解釋。

    人是鐵,飯是剛,吃飯是剛需,要吃飯自然就少不了碗筷,但是沒有規(guī)定碗只能盛飯,除了盛飯它還能盛湯、盛菜,制造者只造這個碗,不關心碗盛什么,具體要盛什么由使用者來決定,這就是泛型的概念。

    泛型就是在定義類、接口、方法的時候指定某一種特定類型(),讓類、接口、方法的使用者來決定具體用哪一種類型的參數(shù)(盛的東西)。

    Java的泛型是在1.5引入的,只在編譯期做泛型檢查,運行期泛型就會消失,我們把這稱為“泛型擦除”,最終類型都會變成 Object。

    在沒有泛型之前,從集合中讀取到的每一個對象都必須進行類型轉換,如果不小心插入了錯誤的類型對象,在運行時的轉換處理就會出錯,有了泛型后,你可以告訴編譯器每個集合接收的對象類型是什么,編譯器在編譯期就會做類型檢查,告知是否插入了錯誤類型的對象,使得程序更加安全,也更加清楚。

    最后插一句,泛型擦除與原生態(tài)類型(List就是原生態(tài),List非原生態(tài))是為了照顧1.5以前設計上的缺陷,為兼容非泛型代碼,所作出的折中策略,所以不推薦使用原生態(tài)類型,如果使用了原生態(tài)類型,就失去了泛型在安全性與描述性方面的優(yōu)勢。

    泛型類

    類上定義泛型,作用于類的成員變量與函數(shù),代碼實例如下

    public class GenericClass<T>{
        //成員變量
        private T t;
        
        public  void function(T t){

        }
        
        public T functionTwo(T t){
            //注意,這個不是泛型方法?。?!
           return t;
        }
    }

    泛型接口

    接口上定義泛型,作用于函數(shù),代碼實例如下

    public interface GenericInterface<T{
        
        public T get();
        
        public void set(T t);

        public T delete(T t);
        
        default T defaultFunction(T t){
            return t;
        }
    }

    泛型函數(shù)

    函數(shù)返回類型旁加上泛型,作用于函數(shù),代碼實例如下

    public class GenericFunction {

        public <T> void function(T t) {
        }

        public <T> functionTwo(T t) {
            return t;
        }

        public <T> String functionThree(T t) {
            return "";
        }
    }

    通配符

    通配符是為了讓Java泛型支持范圍限定,這樣使得泛型的靈活性提升,同時也讓通用性設計有了更多的空間。

    • <?>:無界通配符,即類型不確定,任意類型
    • <? extends T>:上邊界通配符,即?是繼承自T的任意子類型,遵守只讀不寫
    • <? super T>:下邊界通配符,即?T的任意父類型,遵守只寫不讀

    相信大部分人,都是倒在通配符這塊,這里多嘮叨點,「通配符限定的范圍是體現(xiàn)在確認“參數(shù)化類型”的時候,而不是“參數(shù)化類型”填充后」,可能這句話不太好理解,來看看下面的代碼

    /**
     * 1.創(chuàng)建泛型為Number的List類,Integer、Double、Long等都是Number的子類
     *   new ArrayList<>() 等價于 new ArrayList<Number>()
     */

    List<Number> numberList = new ArrayList<Number>();

    /**
     * 2.添加不同子類
     */

    numberList.add(1);//添加Integer類型
    numberList.add(0.5);//添加Double類型
    numberList.add(10000L);//添加Long類型

    /**
     * 3.創(chuàng)建泛型為Number的List類,Integer、Double、Long等都是Number的子類
     *   引用是泛型類別是Number,但具體實現(xiàn)指定的泛型是Integer
     */

    List<Number> numberListTwo = new ArrayList<Integer>();//err 異常編譯不通過

    /**
     * 4.創(chuàng)建泛型為Integer的List類,把該對象的引用地址指向泛型為Number的List
     */

    List<Integer> integerList = new ArrayList<Integer>();
    List<Number> numberListThree = integerList;//err 異常編譯不通過

    • 第一步:我們創(chuàng)建一個泛型為NumberList,編譯器檢查泛型類別是否一致,一致編譯通過(確認參數(shù)化類型)
    • 第二步:泛型Number已經(jīng)填充完畢,調用add函數(shù),此時add入?yún)⒎盒?code style="overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">T已經(jīng)填充為Number,add可入?yún)?code style="overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">Number或其子類
    • 第三步:我們又創(chuàng)建一個泛型為NumberList,編譯器檢查泛型類別是否一致,不一致編譯失敗,提示錯誤(確認參數(shù)化類型)
    • 第四步:其實與第三步一樣,只是做了一個間接的引用(確認參數(shù)化類型)

    如果要解決上面的編譯不通過問題,就需要使用通配符,代碼如下

    /**
     * 1.上邊界通配符,Number與Number子類
     */

    List<? extends Number> numberListFour = new ArrayList<Number>();
    numberListFour = new ArrayList<Integer>();
    numberListFour = new ArrayList<Double>();
    numberListFour = new ArrayList<Long>();

    /**
     * 2.下邊界通配符,Integer與Integer父類
     */

    List<? super Integer> integerList = new ArrayList<Integer>();
    integerList = new ArrayList<Number>();
    integerList = new ArrayList<Object>();

    /**
     * 3. 無界通配符,類型不確定,任意類型
     */

    List<?> list = new ArrayList<Integer>();
    list = new ArrayList<Number>();
    list = new ArrayList<Object>();
    list = new ArrayList<String>();


    最后再來說上邊界通配符只讀不寫,下邊界通配符只寫不讀到底是什么意思,用最簡單的話來說

    • <? extends T>上邊界通配符不作為函數(shù)入?yún)?,只作為函?shù)返回類型,比如List<? extends T>的使用add函數(shù)會編譯不通過,get函數(shù)則沒問題
    • <? super T>下邊界通配符不作為函數(shù)返回類型,只作為函數(shù)入?yún)?,比?code style="overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">List<? super T>的add函數(shù)正常調用,get函數(shù)也沒問題,但只會返回Object,所以意義不大

    大家只需要記住上面的規(guī)則即可,如果想知道為什么這樣設計,可以去了解下P E C S (producer-extends,consumer-super)原則

    最佳實踐

    相信過完基礎理論大家很多東西都回憶起來了,不要著急,現(xiàn)在開始進入正題,后面內容會有大量的代碼實踐,所以大家要坐穩(wěn)了,別暈車了,暈車的話多看幾遍,或者評論區(qū)提出你的疑問~

    無限通配符場景

    使用泛型,類型參數(shù)不確定并且不關心實際的類型參數(shù),就可以使用<?>,像下面的代碼

    /**
     * 獲取集合長度
     */

    public static <T> int size(Collection<T> list){
        return list.size();
    }

    /**
     * 獲取集合長度-2
     */

    public static int sizeTwo(Collection<?>  list){
        return list.size();
    }


    /**
     * 獲取任意Set兩個集合交集數(shù)量
     */

    public static <T,T2> int beMixedSum(Set<T> s1,Set<T2> s2){
        int i = 0;
        for (T t : s1) {
            if (s2.contains(t)) {
                i++;
            }
        }
        return i;
    }

    /**
     * 獲取任意兩個Set集合交集數(shù)量-2
     */

    public static  int beMixedSumTwo(Set<?> s1,Set<?> s2){
        int i = 0;
        for (Object o : s1) {
            if (s2.contains(o)) {
                i++;
            }
        }
        return i;
    }

    sizesizeTwo這兩個函數(shù)都可以正常使用,但是站在設計的角度,sizeTwo會更合適,函數(shù)的目標是返回任意集合的長度,入?yún)⒉捎?code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);"><T>或<?>都可以接收,但是函數(shù)本身并不關心你是什么類型參數(shù),僅僅只要返回長度即可,所以采用<?>。

    beMixedSumbeMixedSumTwo這兩個函數(shù)比較,道理同上面一樣,beMixedSumTwo會更合適,函數(shù)的目標是返回兩個任意Set集合的交集數(shù)量,beMixedSum函數(shù)雖然內部有使用到<T>,但是意義不大,因為contains入?yún)⑹?code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">Object,函數(shù)本身并不關心你是什么類型參數(shù),所以采用<?>。

    忘了補充另一個場景,就是原生態(tài)類型,上述代碼使用原生態(tài)類型函數(shù)使用也沒問題,但是強烈不推薦,因為使用原生態(tài)就丟失了泛型帶來的安全性與描述性!?。?/span>

    上下邊界通配符場景

    首先泛型是不變的,換句話說List<Object> != List<String>,有時候需要更多靈活性,就可以通過上下邊界通配符來做提升。

    /**
     * 集合工具類
     */

    public class CollectionUtils<T>{
            
        /**
         * 復制集合-泛型
         */

        public List<T>  listCopy(Collection<T> collection){
            List<T> newCollection = new ArrayList<>();
            for (T t : collection) {
                newCollection.add(t);
            }
            return newCollection;
        }
        
    }

    上面聲明了一個CollectionUtils類,擁有listCopy方法,傳入任意一個集合返回新的集合,看似沒有什么問題,也很靈活,那再看看下面這段代碼。

    public static void main(String[] agrs){
        CollectionUtils<Number> collectionUtils = new CollectionUtils<>();
        List<Number>  list = new ArrayList<>();
        //list.add....
        List<Integer>  listTwo = new ArrayList<>();
        //listTwo.add....
        List<Double>  listThree = new ArrayList<>();
        //listThree.add....

        List<Number> list1 = collectionUtils.listCopy(list);
        list1 = collectionUtils.listCopy(listTwo);//err 編譯異常
        list1 = collectionUtils.listCopy(listThree);//err 編譯異常
    }

    創(chuàng)建CollectionUtils類,泛型的類型參數(shù)為Number,listCopy函數(shù)入?yún)⒌姆盒吞畛錇?code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">Number,此時listCopy只支持泛型為NumberList,如果要讓它同時支持泛型為Number子類的List,就需要使用上邊界通配符,我們再追加一個方法

    /**
     * 集合工具
     */

    public class CollectionUtils<T>{
            
        /**
         * 復制集合-泛型
         */

        public List<T>  listCopy(Collection<T> collection){
            List<T> newCollection = new ArrayList<>();
            for (T t : collection) {
                newCollection.add(t);
            }
            return newCollection;
        }
        
        /**
         * 復制集合-上邊界通配符
         */

        public  List<T>  listCopyTwo(Collection<? extends T> collection){
            List<T> newCollection = new ArrayList<>();
            for (T t : collection) {
                newCollection.add(t);

            }
            return newCollection;
        }
    }

    public static void main(String[] agrs){
        CollectionUtils<Number> collectionUtils = new CollectionUtils<>();
        List<Number>  list = new ArrayList<>();
        //list.add....
        List<Integer>  listTwo = new ArrayList<>();
        //listTwo.add....
        List<Double>  listThree = new ArrayList<>();
        //listThree.add....

        List<Number> list1 = collectionUtils.listCopyTwo(list);
        list1 = collectionUtils.listCopyTwo(listTwo);
        list1 = collectionUtils.listCopyTwo(listThree);
    }

    現(xiàn)在使用listCopyTwo就沒有問題,listCopyTwo對比listCopy它的適用范圍更廣泛也更靈活,listCopy能做的listCopyTwo能做,listCopyTwo能做的listCopy就不一定能做了,除此之外,細心的小伙伴肯定發(fā)現(xiàn)了,使用上邊界通配符的collection在函數(shù)內只使用到了讀操作,遵循了只讀不寫原則。

    看完了上邊界通配符,再來看看下邊界通配符,依然是復制方法



    /**
     * 兒子
     */

    public class Son extends Father{}

    /**
     * 父親
     */

    public class Father  extends  Grandpa{}

    /**
     * 爺爺
     */

    public class Grandpa {}

    /**
     * 集合工具
     */

    public class CollectionUtils<T>{

        /**
         * 復制集合-泛型
         * target目標   src來源
         */

       public void copy(List<T> target,List<T> src){
            if (src.size() > target.size()){
                for (int i = 0; i < src.size(); i++) {
                    target.set(i,src.get(i));
                }
            }
        }
        
    }

    定義了3個類,分別是Son兒子、Father父親、Grandpa爺爺,它們是繼承關系,作為集合元素,還聲明了一個CollectionUtils類,擁有copy方法,傳入兩個集合,目標集合與來源集合,把來源集合元素復制到目標集合中,再看看下面這段代碼

    public static void main(String[] agrs){
        CollectionUtils<Father> collectionUtils = new CollectionUtils<>();

        List<Father>  fatherTargets = new ArrayList<>();
        List<Father>  fatherSources = new ArrayList<>();
        //fatherSources.add...
        collectionUtils.copy(fatherTargets,fatherSources);
        
        //子類復制到父類
        List<Son> sonSources = new ArrayList<>();
        //sonSources.add...
        collectionUtils.copy(fatherTargets,sonSources);//err 編譯異常
        
    }

    創(chuàng)建CollectionUtils類,泛型的類型參數(shù)為Father父親,copy函數(shù)入?yún)⒌姆盒吞畛錇?code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">Father,此時copy只支持泛型為FatherList,也就說,只支持泛型的類型參數(shù)為Father之間的復制,如果想支持把子類復制到父類要怎么做,先分析下copy函數(shù),copy函數(shù)的入?yún)?code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">src在函數(shù)內部只涉及到了get函數(shù),即讀操作(泛型只作為get函數(shù)返回類型),符合只讀不寫原則,可以采用上邊界通配符,調整代碼如下

    /**
     * 集合工具
     */

    public class CollectionUtils<T>{

        /**
         * 復制集合-泛型
         * target目標   src來源
         */

        public void copy(List<T> target,List<? extends T> src){
            if (src.size() > target.size()){
                for (int i = 0; i < src.size(); i++) {
                    target.set(i,src.get(i));
                }
            }
        }
    }

    public static void main(String[] agrs){
        CollectionUtils<Father> collectionUtils = new CollectionUtils<>();

        List<Father>  fatherTargets = new ArrayList<>();
        List<Father>  fatherSources = new ArrayList<>();
        //fatherSources.add...
        collectionUtils.copy(fatherTargets,fatherSources);
        
        //子類復制到父類
        List<Son> sonSources = new ArrayList<>();
        //sonSources.add...
        collectionUtils.copy(fatherTargets,sonSources);
        
        //把子類復制到父類的父類
        List<Grandpa> grandpaTargets = new ArrayList<>();
        collectionUtils.copy(grandpaTargets,sonSources);//err 編譯異常
    }

    src入?yún)⒄{整為上邊界通配符后,copy函數(shù)傳入List<Son> sonSources就沒問題了,此時的copy函數(shù)相較之前的更加靈活了,支持同類與父子類復制,接著又發(fā)現(xiàn)了一個問題,目前能復制到上一級父類,如果是多級父類,還無法支持,繼續(xù)分析copy函數(shù),copy函數(shù)的入?yún)?code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(255, 100, 65);">target在函數(shù)內部只涉及到了add函數(shù),即寫操作(泛型只作為add函數(shù)入?yún)?/span>),符合只寫不讀原則,可以采用下邊界通配符,調整代碼如下

    /**
     * 集合工具
     */

    public class CollectionUtils<T>{

        /**
         * 復制集合-泛型
         * target目標   src來源
         */

        public void copy(List<? super T>  target,List<? extends T> src){
            if (src.size() > target.size()){
                for (int i = 0; i < src.size(); i++) {
                    target.set(i,src.get(i));
                }
            }
        }
    }

    public static void main(String[] agrs){
        CollectionUtils<Father> collectionUtils = new CollectionUtils<>();

        List<Father>  fatherTargets = new ArrayList<>();
        List<Father>  fatherSources = new ArrayList<>();
        //fatherSources.add...
        collectionUtils.copy(fatherTargets,fatherSources);
        
        //子類復制到父類
        List<Son> sonSources = new ArrayList<>();
        //sonSources.add...
        collectionUtils.copy(fatherTargets,sonSources);
        
        //把子類復制到父類的父類
        List<Grandpa> grandpaTargets = new ArrayList<>();
        collectionUtils.copy(grandpaTargets,sonSources);
    }

    copy函數(shù)終于是完善了,可以說現(xiàn)在是真正支持父子類復制,不難發(fā)現(xiàn)copy函數(shù)的設計還是遵循通配符原則的,target作為目標集合,只做寫入,符合只寫不讀原則,采用了下邊界通配符,src作為來源集合,只做讀取,符合只讀不寫原則,采用了上邊界通配符,最后設計出來的copy函數(shù),它的靈活性與適用范圍是遠超<T>方式設計的。

    最后總結一下,什么時候用通配符,如果參數(shù)泛型類即要讀也要寫,那么就不推薦使用,使用正常的泛型即可,如果參數(shù)泛型類只讀或寫,就可以根據(jù)原則采用對應的上下邊界,是不是十分簡單,最后再說一次讀寫的含義,這塊確實很容易暈

    • 讀:所謂讀是指參數(shù)泛型類,泛型只作為該參數(shù)類的函數(shù)返回類型,那這個函數(shù)就是讀,List作為參數(shù)泛型類,它的get函數(shù)就是讀
    • 寫:所謂寫是指參數(shù)泛型類,泛型只作為該參數(shù)類的函數(shù)入?yún)ⅲ沁@個函數(shù)就是寫,List作為參數(shù)泛型類,它的add函數(shù)就是讀

    留給小題,大家可以思考下StreamforEach函數(shù)與map函數(shù)的設計,在Java1.8 Stream中是大量用到了通配符設計

    -----------------------------------------------------------------
    /**
     * 下邊界通配符
     */

    void forEach(Consumer<? super T> action);

    public interface Consumer<T{

        //寫方法
        void accept(T t);
    }

    -----------------------------------------------------------------
    /**
     * 上下邊界通配符
     */

    <R> Stream<R> map(Function<? super T, ? extends R> mapper)

    public interface Function<T, R> 
    {

         //讀寫方法,T只作為入?yún)⒎蠈懀琑只作為返回值,符合讀
        apply(T t);
    }
    -----------------------------------------------------------------

    //代碼案例
    public static void main(String[] agrs) {
            
            List<Father> fatherList = new ArrayList<>();
            
            Consumer<? super Father> action = new Consumer<Father>() {
                @Override
                public void accept(Father father) {
                    //執(zhí)行father邏輯
                }
            };
            
             //下邊界通配符向上轉型
            Consumer<? super Father> actionTwo = new Consumer<Grandpa>() {
                @Override
                public void accept(Grandpa grandpa) {
                    //執(zhí)行grandpa邏輯
                }
            };
            
             Function<? super Father, ? extends Grandpa> mapper = new Function<Father, Grandpa>() {
                @Override
                public Grandpa apply(Father father) {
                    //執(zhí)行father邏輯后返回Grandpa
                    return new Grandpa();
                }
            };
            
            //下邊界通配符向上轉型,上邊界通配符向下轉型
             Function<? super Father, ? extends Grandpa> mapperTwo = new Function<Grandpa, Son>() {
                @Override
                public Son apply(Grandpa grandpa) {
                    //執(zhí)行grandpa邏輯后,返回Son
                    return new Son();
                }
            };
            
            fatherList.stream().forEach(action);
            fatherList.stream().forEach(actionTwo);
            
            fatherList.stream().map(mapper);
            fatherList.stream().map(mapperTwo);
            
        
        }
    -----------------------------------------------------------------

    有限制泛型場景

    有限制泛型很簡單了,應用場景就是你需要對泛型的參數(shù)類型做限制,就可以使用它,比如下面這段代碼

    public class GenericClass<T extends Grandpa{
        
       
        public void test(T t){
            //....
        }

    }

    public static void main(String[] agrs){
        GenericClass<Grandpa> grandpaGeneric = new GenericClass<>();
        grandpaGeneric.test(new Grandpa());
        grandpaGeneric.test(new Father());
        grandpaGeneric.test(new Son());
        
        GenericClass<Father> fatherGeneric = new GenericClass<>();
        fatherGeneric.test(new Father());
        fatherGeneric.test(new Son());

        GenericClass<Son> sonGeneric = new GenericClass<>();
        sonGeneric.test(new Son());
        
        GenericClass<Object> ObjectGeneric = new GenericClass<>();//err 編譯異常
        
    }

    GenericClass泛型參數(shù)化類型被限制為Grandpa或其子類,就這么簡單,千萬不要把有限制泛型與上邊界通配符搞混了,這兩個不是同一個東西(<T extends Grandpa> != <? extends Grandpa>),<T extends Grandpa>不需要遵循上邊界通配符的原則,它就是簡單的泛型參數(shù)化類型限制,而且沒有super的寫法。

    遞歸泛型場景

    在有限制泛型的基礎上,又可以衍生出遞歸泛型,就是自身需要使用到自身,比如集合進行自定義元素大小比較的時候,通常會配合Comparable接口來完成,看看下面這段代碼

    public class Person implements Comparable<Person{

        private int age;

        public Person(int age) {
            this.age = age;
        }

        public int getAge() {
            return age;
        }

        @Override
        public int compareTo(Person o) {
            // 0代表相等 1代表大于  <0代表小于    
            return this.age - o.age;
        }
    }


    /**
     * 集合工具
     */

    public class CollectionUtils{
        
        /**
         * 獲取集合最大值
         */

        public static  <E extends Comparable<E>> max(List<E> list){
            E result = null;
            for (E e : list) {
                 if (result == null || e.compareTo(result) > 0){
                     result = e;
                 }
            }
            return result;
        }
    }


    public static void main(String[] agrs){

        List<Person> personList = new ArrayList<>();
        personList.add(new Person(12));
        personList.add(new Person(19));
        personList.add(new Person(20));
        personList.add(new Person(5));
        personList.add(new Person(18));
        //返回年齡最大的Person元素 
        Person max = CollectionUtils.max(personList);

    }

    重點關注max泛型函數(shù),max泛型函數(shù)的目標是返回集合最大的元素,內部比較元素大小,取最大值返回,也就說需要和同類型元素做比較,<E extends Comparable<E>>含義是,泛型E必須是Comparable或其子類/實現(xiàn)類,因為比較元素是同類型,所以Comparable泛型也是E,最終接收的List泛型參數(shù)化類型必須實現(xiàn)了Comparable接口,并且Comparable接口填充的泛型也是該參數(shù)化類型,就像上述代碼一樣。

    —————END—————

    推薦閱讀:


    最近面試BAT,整理一份面試資料Java面試BAT通關手冊,覆蓋了Java核心技術、JVM、Java并發(fā)、SSM、微服務、數(shù)據(jù)庫、數(shù)據(jù)結構等等。
    獲取方式:關注公眾號并回復 java 領取,更多內容陸續(xù)奉上。
    明天見(??ω??)??
    瀏覽 23
    點贊
    評論
    收藏
    分享

    手機掃一掃分享

    分享
    舉報
    評論
    圖片
    表情
    推薦
    點贊
    評論
    收藏
    分享

    手機掃一掃分享

    分享
    舉報

    <kbd id="5sdj3"></kbd>
    <th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>
    一逼综合| 男女激情视频网站 | 亚洲AV蜜桃永久无码精品色哟 | 欧美性色15P | 国产视频成人 |