<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          一文通關(guān)苦澀難懂的Java泛型

          共 27499字,需瀏覽 55分鐘

           ·

          2021-04-08 10:05

          前言

          相信大家對(duì)Java泛型并不陌生,無(wú)論是開(kāi)源框架還是JDK源碼都能看到它,毫不夸張的說(shuō),泛型是通用設(shè)計(jì)上必不可少的元素,所以真正理解與正確使用泛型,是一門(mén)必修課,本文將解開(kāi)大家對(duì)泛型的疑惑,并通過(guò)大量實(shí)踐,讓你get到泛型正確的使用姿勢(shì),下面開(kāi)始進(jìn)入正題吧!

          大綱

          基礎(chǔ)

          因?yàn)楸疚闹貙?shí)踐,而且面對(duì)的是Java開(kāi)發(fā)人員群體,大家對(duì)泛型都有基礎(chǔ),所以泛型基礎(chǔ)這塊會(huì)快速過(guò),幫助大家回憶下即可,后面主要的則重點(diǎn)是通配符

          編譯期與運(yùn)行期

          編譯期是指把源碼交給編譯器編譯成計(jì)算機(jī)可執(zhí)行文件的過(guò)程,運(yùn)行期是指把編譯后的文件交給計(jì)算機(jī)執(zhí)行,直到程序結(jié)束。

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

          泛型

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

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

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

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

          在沒(méi)有泛型之前,從集合中讀取到的每一個(gè)對(duì)象都必須進(jìn)行類型轉(zhuǎn)換,如果不小心插入了錯(cuò)誤的類型對(duì)象,在運(yùn)行時(shí)的轉(zhuǎn)換處理就會(huì)出錯(cuò),有了泛型后,你可以告訴編譯器每個(gè)集合接收的對(duì)象類型是什么,編譯器在編譯期就會(huì)做類型檢查,告知是否插入了錯(cuò)誤類型的對(duì)象,使得程序更加安全,也更加清楚。

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

          泛型類

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

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

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

          泛型接口

          接口上定義泛型,作用于函數(shù),代碼實(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ù),代碼實(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泛型支持范圍限定,這樣使得泛型的靈活性提升,同時(shí)也讓通用性設(shè)計(jì)有了更多的空間。

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

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

          /**
           * 1.創(chuàng)建泛型為Number的List類,Integer、Double、Long等都是Number的子類
           *   new ArrayList<>() 等價(jià)于 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,但具體實(shí)現(xiàn)指定的泛型是Integer
           */

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

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

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

          • 第一步:我們創(chuàng)建一個(gè)泛型為NumberList,編譯器檢查泛型類別是否一致,一致編譯通過(guò)(確認(rèn)參數(shù)化類型)
          • 第二步:泛型Number已經(jīng)填充完畢,調(diào)用add函數(shù),此時(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)填充為Numberadd可入?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)建一個(gè)泛型為NumberList,編譯器檢查泛型類別是否一致,不一致編譯失敗,提示錯(cuò)誤(確認(rèn)參數(shù)化類型)
          • 第四步:其實(shí)與第三步一樣,只是做了一個(gè)間接的引用(確認(rèn)參數(shù)化類型)

          如果要解決上面的編譯不通過(guò)問(wèn)題,就需要使用通配符,代碼如下

          /**
           * 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. 無(wú)界通配符,類型不確定,任意類型
           */

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


          最后再來(lái)說(shuō)上邊界通配符只讀不寫(xiě),下邊界通配符只寫(xiě)不讀到底是什么意思,用最簡(jiǎn)單的話來(lái)說(shuō)

          • <? extends T>上邊界通配符不作為函數(shù)入?yún)ⅲ蛔鳛楹瘮?shù)返回類型,比如List<? extends T>的使用add函數(shù)會(huì)編譯不通過(guò),get函數(shù)則沒(méi)問(wèn)題
          • <? 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ù)正常調(diào)用,get函數(shù)也沒(méi)問(wèn)題,但只會(huì)返回Object,所以意義不大

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

          最佳實(shí)踐

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

          無(wú)限通配符場(chǎng)景

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

          /**
           * 獲取集合長(zhǎng)度
           */

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

          /**
           * 獲取集合長(zhǎng)度-2
           */

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


          /**
           * 獲取任意Set兩個(gè)集合交集數(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;
          }

          /**
           * 獲取任意兩個(gè)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這兩個(gè)函數(shù)都可以正常使用,但是站在設(shè)計(jì)的角度,sizeTwo會(huì)更合適,函數(shù)的目標(biāo)是返回任意集合的長(zhǎng)度,入?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ù)本身并不關(guān)心你是什么類型參數(shù),僅僅只要返回長(zhǎng)度即可,所以采用<?>

          beMixedSumbeMixedSumTwo這兩個(gè)函數(shù)比較,道理同上面一樣,beMixedSumTwo會(huì)更合適,函數(shù)的目標(biāo)是返回兩個(gè)任意Set集合的交集數(shù)量,beMixedSum函數(shù)雖然內(nèi)部有使用到<T>,但是意義不大,因?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);">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ù)本身并不關(guān)心你是什么類型參數(shù),所以采用<?>

          忘了補(bǔ)充另一個(gè)場(chǎng)景,就是原生態(tài)類型,上述代碼使用原生態(tài)類型函數(shù)使用也沒(méi)問(wèn)題,但是強(qiáng)烈不推薦,因?yàn)槭褂迷鷳B(tài)就丟失了泛型帶來(lái)的安全性與描述性!!!

          上下邊界通配符場(chǎng)景

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

          /**
           * 集合工具類
           */

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

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

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

          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ù)為NumberlistCopy函數(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,此時(shí)listCopy只支持泛型為NumberList,如果要讓它同時(shí)支持泛型為Number子類的List,就需要使用上邊界通配符,我們?cè)僮芳右粋€(gè)方法

          /**
           * 集合工具
           */

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

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

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

          看完了上邊界通配符,再來(lái)看看下邊界通配符,依然是復(fù)制方法



          /**
           * 兒子
           */

          public class Son extends Father{}

          /**
           * 父親
           */

          public class Father  extends  Grandpa{}

          /**
           * 爺爺
           */

          public class Grandpa {}

          /**
           * 集合工具
           */

          public class CollectionUtils<T>{

              /**
               * 復(fù)制集合-泛型
               * target目標(biāo)   src來(lái)源
               */

             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個(gè)類,分別是Son兒子、Father父親、Grandpa爺爺,它們是繼承關(guān)系,作為集合元素,還聲明了一個(gè)CollectionUtils類,擁有copy方法,傳入兩個(gè)集合,目標(biāo)集合與來(lái)源集合,把來(lái)源集合元素復(fù)制到目標(biāo)集合中,再看看下面這段代碼

          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);
              
              //子類復(fù)制到父類
              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,此時(shí)copy只支持泛型為FatherList,也就說(shuō),只支持泛型的類型參數(shù)為Father之間的復(fù)制,如果想支持把子類復(fù)制到父類要怎么做,先分析下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ù)內(nèi)部只涉及到了get函數(shù),即讀操作(泛型只作為get函數(shù)返回類型),符合只讀不寫(xiě)原則,可以采用上邊界通配符,調(diào)整代碼如下

          /**
           * 集合工具
           */

          public class CollectionUtils<T>{

              /**
               * 復(fù)制集合-泛型
               * target目標(biāo)   src來(lái)源
               */

              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);
              
              //子類復(fù)制到父類
              List<Son> sonSources = new ArrayList<>();
              //sonSources.add...
              collectionUtils.copy(fatherTargets,sonSources);
              
              //把子類復(fù)制到父類的父類
              List<Grandpa> grandpaTargets = new ArrayList<>();
              collectionUtils.copy(grandpaTargets,sonSources);//err 編譯異常
          }

          src入?yún)⒄{(diào)整為上邊界通配符后,copy函數(shù)傳入List<Son> sonSources就沒(méi)問(wèn)題了,此時(shí)的copy函數(shù)相較之前的更加靈活了,支持同類與父子類復(fù)制,接著又發(fā)現(xiàn)了一個(gè)問(wèn)題,目前能復(fù)制到上一級(jí)父類,如果是多級(jí)父類,還無(wú)法支持,繼續(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ù)內(nèi)部只涉及到了add函數(shù),即寫(xiě)操作(泛型只作為add函數(shù)入?yún)?/span>),符合只寫(xiě)不讀原則,可以采用下邊界通配符,調(diào)整代碼如下

          /**
           * 集合工具
           */

          public class CollectionUtils<T>{

              /**
               * 復(fù)制集合-泛型
               * target目標(biāo)   src來(lái)源
               */

              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);
              
              //子類復(fù)制到父類
              List<Son> sonSources = new ArrayList<>();
              //sonSources.add...
              collectionUtils.copy(fatherTargets,sonSources);
              
              //把子類復(fù)制到父類的父類
              List<Grandpa> grandpaTargets = new ArrayList<>();
              collectionUtils.copy(grandpaTargets,sonSources);
          }

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

          最后總結(jié)一下,什么時(shí)候用通配符,如果參數(shù)泛型類即要讀也要寫(xiě),那么就不推薦使用,使用正常的泛型即可,如果參數(shù)泛型類只讀或?qū)懀涂梢愿鶕?jù)原則采用對(duì)應(yīng)的上下邊界,是不是十分簡(jiǎn)單,最后再說(shuō)一次讀寫(xiě)的含義,這塊確實(shí)很容易暈

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

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

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

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

          public interface Consumer<T{

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

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

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

          public interface Function<T, R> 
          {

               //讀寫(xiě)方法,T只作為入?yún)⒎蠈?xiě),R只作為返回值,符合讀
              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邏輯
                      }
                  };
                  
                   //下邊界通配符向上轉(zhuǎn)型
                  Consumer<? super Father> actionTwo = new Consumer<Grandpa>() {
                      @Override
                      public void accept(Grandpa grandpa) {
                          //執(zhí)行g(shù)randpa邏輯
                      }
                  };
                  
                   Function<? super Father, ? extends Grandpa> mapper = new Function<Father, Grandpa>() {
                      @Override
                      public Grandpa apply(Father father) {
                          //執(zhí)行father邏輯后返回Grandpa
                          return new Grandpa();
                      }
                  };
                  
                  //下邊界通配符向上轉(zhuǎn)型,上邊界通配符向下轉(zhuǎn)型
                   Function<? super Father, ? extends Grandpa> mapperTwo = new Function<Grandpa, Son>() {
                      @Override
                      public Son apply(Grandpa grandpa) {
                          //執(zhí)行g(shù)randpa邏輯后,返回Son
                          return new Son();
                      }
                  };
                  
                  fatherList.stream().forEach(action);
                  fatherList.stream().forEach(actionTwo);
                  
                  fatherList.stream().map(mapper);
                  fatherList.stream().map(mapperTwo);
                  
              
              }
          -----------------------------------------------------------------

          有限制泛型場(chǎng)景

          有限制泛型很簡(jiǎn)單了,應(yīng)用場(chǎng)景就是你需要對(duì)泛型的參數(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或其子類,就這么簡(jiǎn)單,千萬(wàn)不要把有限制泛型與上邊界通配符搞混了,這兩個(gè)不是同一個(gè)東西(<T extends Grandpa> != <? extends Grandpa>),<T extends Grandpa>不需要遵循上邊界通配符的原則,它就是簡(jiǎn)單的泛型參數(shù)化類型限制,而且沒(méi)有super的寫(xiě)法。

          遞歸泛型場(chǎng)景

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

          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);

          }

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

          —————END—————

          推薦閱讀:


          最近面試BAT,整理一份面試資料Java面試BAT通關(guān)手冊(cè),覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫(kù)、數(shù)據(jù)結(jié)構(gòu)等等。
          獲取方式:關(guān)注公眾號(hào)并回復(fù) java 領(lǐng)取,更多內(nèi)容陸續(xù)奉上。
          明天見(jiàn)(??ω??)??
          瀏覽 23
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  婷婷五月天在线无码 | 日韩黄页网站大全免费在线观看 | 成人免费一级毛片在线播放视频 | 成年人电影久久 | 黄片在线免费视频 |