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

          Java基礎(chǔ)不簡單,泛型很重要!

          共 541字,需瀏覽 2分鐘

           ·

          2021-10-19 06:57

          前言

          其實(shí)在開發(fā)中經(jīng)常會(huì)看到泛型的使用,但是很多人對(duì)其也是一知半解,大概知道這是一個(gè)類似標(biāo)簽的東西。比如最常見的給集合定義泛型。

          List?list?=?new?ArrayList<>();
          Map?map?=?new?HashMap<>();

          那么什么是泛型,為什么使用泛型,怎么使用泛型,接著往下看。

          什么是泛型

          Java泛型是J2SE1.5中引入的一個(gè)新特性,其本質(zhì)是參數(shù)化類型,也就是說所操作的數(shù)據(jù)類型被指定為一個(gè)參數(shù)(type parameter),這種參數(shù)類型可以用在類、接口和方法的創(chuàng)建中,分別稱為泛型類、泛型接口、泛型方法。-- 百度百科

          這句話讀起來有點(diǎn)拗口,但是我們要抓住他說的關(guān)鍵,參數(shù)化類型可以用在類、接口和方法的創(chuàng)建中,我們知道泛型是在什么地方使用。

          為什么使用泛型

          一般我在思考這種問題時(shí),會(huì)反過來思考,假如沒有泛型會(huì)怎么樣?

          我們以最簡單的List集合為例子,假如沒有泛型:

          public?static?void?main(String[]?args)?{
          ????List?list?=?new?ArrayList();
          ????list.add("good");
          ????list.add(100);
          ????list.add('a');
          ????for(int?i?=?0;?i?????????String?val?=?(String)?list.get(i);
          ????????System.out.println("val:"?+?val);
          ????}
          }

          很顯然在沒有泛型的時(shí)候,List默認(rèn)是Object類型,所以List里的元素可以是任意的,看起來集合里裝著任意類型的參數(shù)是“挺不錯(cuò)”,但是任意的類型的缺點(diǎn)也是很明顯的,就是要開發(fā)者對(duì)集合中的元素類型在預(yù)知的情況下進(jìn)行操作,否則編譯時(shí)不會(huì)提示錯(cuò)誤,但是運(yùn)行時(shí)很容易出現(xiàn)類型轉(zhuǎn)換異常(ClassCastException)。

          如果沒有泛型,第二個(gè)小問題是,我們把一個(gè)對(duì)象放進(jìn)了集合中,但是集合并不會(huì)記住這個(gè)對(duì)象的類型,再次取出時(shí)統(tǒng)統(tǒng)都會(huì)變成Object類,但是在運(yùn)行時(shí)仍然為其本身的類型。

          所以引入泛型就可以解決以上兩個(gè)問題:

          • 類型安全問題。使用泛型,則會(huì)在編譯期就能發(fā)現(xiàn)類型轉(zhuǎn)換異常的錯(cuò)誤。
          • 消除類型強(qiáng)轉(zhuǎn)。泛型可以消除源代碼中的許多強(qiáng)轉(zhuǎn)類型的操作,這樣可以使代碼更加可讀,并減少出錯(cuò)的機(jī)會(huì)。
          泛型的特性

          泛型只有在編譯階段有效,在運(yùn)行階段會(huì)被擦除。

          下面做個(gè)試驗(yàn),請(qǐng)看代碼:

          List?stringArrayList?=?new?ArrayList();
          List?integerArrayList?=?new?ArrayList();

          Class?classStringArrayList?=?stringArrayList.getClass();
          Class?classIntegerArrayList?=?integerArrayList.getClass();
          System.out.println(classStringArrayList?==?classIntegerArrayList);

          結(jié)果是true,由此可看出,在運(yùn)行時(shí)ArrayListArrayList都會(huì)被擦除成ArrayList

          Java 泛型擦除是 Java 泛型中的一個(gè)重要特性,其目的是避免過多的創(chuàng)建類而造成的運(yùn)行時(shí)的過度消耗。

          泛型的使用方式

          在上文也提到泛型有三種使用方式:泛型類、泛型接口、泛型方法。

          泛型類

          基本語法:

          public?class?類名<泛型標(biāo)識(shí),?泛型標(biāo)識(shí),?...>?{
          ????private?泛型標(biāo)識(shí)?變量名;
          }

          示例代碼:

          public?class?GenericClass<T>?{
          ????private?T?t;
          }

          在泛型類里面,泛型形參T可用在返回值和方法參數(shù)上,例如:

          public?class?GenericClass<T>?{

          ????private?T?t;

          ????public?GenericClass()?{
          ????}

          ????public?void?setValue(T?t)?{//作為參數(shù)
          ????????this.t?=?t;
          ????}

          ????public?T?getValue()?{//作為返回值
          ????????return?t;
          ????}
          }

          當(dāng)我們創(chuàng)建類實(shí)例時(shí),就可以傳入類型實(shí)參:

          public?static?void?main(String[]?args)?throws?Exception?{
          ????//泛型傳入了String類型
          ????GenericClass?generic?=?new?GenericClass();
          ????//這里就限制了setValue()方法只能傳入String類型
          ????generic.setValue("abc");
          ????//限制了getValue()方法得到的也是String類型
          ????String?value?=?generic.getValue();
          ????System.out.println(value);
          }

          這里與普通類創(chuàng)建實(shí)例不同的地方在于,泛型類的構(gòu)造需要在類名后面添加上<泛型>,這個(gè)尖括號(hào)傳的是什么類型,T就代表什么類型。泛型類的作用就體現(xiàn)在這里,他限制了這個(gè)類的使用了泛型標(biāo)識(shí)的方法的返回值和參數(shù)。

          泛型方法

          基本語法:

          修飾符??返回值類型?方法名(形參列表){
          }

          示例:

          //靜態(tài)方法
          public?static??E?getObject1(Class?clazz)?throws?Exception?{
          ????return?clazz.newInstance();
          }

          //實(shí)例方法
          public??E?getObject2(Class?clazz)?throws?Exception?{
          ????return?clazz.newInstance();
          }

          使用示例:

          public?static?void?main(String[]?args)?throws?Exception?{
          ????GenericClass?generic?=?new?GenericClass<>();
          ????Object?object1?=?GenericClass.getObject1(Object.class);
          ????System.out.println(object1);
          ????Object?object2?=?generic.getObject2(Object.class);
          ????System.out.println(object2);
          }

          其實(shí)泛型方法比較簡單,就是在返回值前加上尖括號(hào)<泛型標(biāo)識(shí)>來表示泛型變量。不過對(duì)于初學(xué)者來說,很容易會(huì)跟泛型類的泛型方法混淆,特別是泛型類里定義了泛型方法的情況。

          public?class?GenericClass<T>?{
          ????private?T?t;

          ????public?GenericClass()?{
          ????}
          ?//泛型類的方法
          ????public?void?setValue(T?t)?{
          ????????this.t?=?t;
          ????}
          ?//泛型類的方法
          ????public?T?getValue()?{
          ????????return?t;
          ????}
          ?//靜態(tài)泛型方法,區(qū)別在于在返回值前需要加上尖括號(hào)<泛型標(biāo)識(shí)>
          ????public?static??E?getObject1(Class?clazz)?throws?Exception?{
          ????????return?clazz.newInstance();
          ????}
          ?//實(shí)例泛型方法,區(qū)別在于在返回值前需要加上尖括號(hào)<泛型標(biāo)識(shí)>
          ????public??E?getObject2(Class?clazz)?throws?Exception?{
          ????????return?clazz.newInstance();
          ????}
          }

          泛型接口

          基本語法:

          public?接口名稱?<泛型標(biāo)識(shí),?泛型標(biāo)識(shí),?...>{
          ????泛型標(biāo)識(shí)?方法名();
          }

          示例:

          public?interface?Generator<T>?{
          ????T?next();
          }

          當(dāng)實(shí)現(xiàn)泛型接口不傳入實(shí)參時(shí),與泛型類定義相同,需要將泛型的聲明加在類名后面:

          public?class?ObjectGenerator<T>?implements?Generator<T>?{
          ????@Override
          ????public?T?next()?{
          ????????return?null;
          ????}
          }

          當(dāng)實(shí)現(xiàn)泛型接口傳入實(shí)參時(shí),當(dāng)泛型參數(shù)傳入了具體的實(shí)參后,則所有使用到泛型的地方都會(huì)被替換成傳入的參數(shù)類型。比如下面的例子中,next()方法的返回值就變成了Integer類型:

          public?class?IntegerGenerator?implements?Generator<Integer>?{
          ????@Override
          ????public?Integer?next()?{
          ????????Random?random?=?new?Random();
          ????????return?random.nextInt(10);
          ????}
          }

          泛型通配符

          類型通配符一般是使用 ? 代替具體的類型實(shí)參,這里的 ? 是具體的類型實(shí)參,而不是類型形參。它跟Integer,Double,String這些類型一樣都是一種實(shí)際的類型,我們可以把 ? 看作是所有類型的父類。

          類型通配符又可以分為類型通配符上限和類型通配符下限。

          類型通配符上限

          基本語法:

          類/接口

          要求該泛型的類型,只能是實(shí)參類型,或?qū)崊㈩愋偷?子類 類型。

          比如:

          public?class?ListUtils<T?extends?List>?{
          ????public?void?doing(T?t)?{
          ????????System.out.println("size:?"?+?t.size());
          ????????for?(Object?o?:?t)?{
          ????????????System.out.println(o);
          ????????}
          ????}
          }

          這樣就限制了傳入的泛型只能是List的子類,如下所示:

          public?static?void?main(String[]?args)?throws?Exception?{
          ????ListUtils?utils?=?new?ListUtils<>();
          ????List?list?=?Arrays.asList("1",?"2",?"3");
          ????utils.doing(list);
          }

          類型通配符下限

          基本語法:

          類/接口super?實(shí)參類型>

          要求該泛型的類型,只能是實(shí)參類型,或?qū)崊㈩愋偷?父類 類型

          比如:

          public?class?ListUtils?{
          ????//靜態(tài)泛型方法,使用super關(guān)鍵字定義通配符下限
          ????public?static??void?copy(Collectionsuper?E>?parentList,?Collection?childList)?{
          ????????for?(E?e?:?childList)?{
          ????????????parentList.add(e);
          ????????}
          ????}
          }

          限制了傳入的List類型:

          public?static?void?main(String[]?args)?throws?Exception?{
          ????Integer?i1?=?new?Integer(10);
          ????Integer?i2?=?new?Integer(20);
          ????List?integers?=?new?ArrayList<>();
          ????integers.add(i1);
          ????integers.add(i2);
          ????List?numbers?=?new?ArrayList<>();
          ????//childList傳入的是List,所以parentList傳入的只能是Integer本身或者其父類的List
          ????ListUtils.copy(numbers,?integers);
          ????System.out.println(numbers);//[10,?20]
          }

          泛型使用注意點(diǎn)

          不能在靜態(tài)字段使用泛型。

          096c9572f8412e7fdbe8edead1c525b5.webp

          不能和基本類型一起使用。

          ee9d21cf467485d0352e1916b255afeb.webp

          異常類不能使用泛型。

          3f2a1f185581858d195692d1df40ba10.webp總結(jié)

          最后總結(jié)一下泛型的作用:

          • 提高了代碼的可讀性,這點(diǎn)毋庸置疑。
          • 解決類型安全的問題,在編譯期就能發(fā)現(xiàn)類型轉(zhuǎn)換異常的錯(cuò)誤。
          • 可拓展性強(qiáng),可以使用泛型通配符定義,不需要定義實(shí)際的數(shù)據(jù)類型。
          • 提高了代碼的重用性。可以重用數(shù)據(jù)處理算法,不需要為每一種類型都提供特定的代碼。

          非常感謝你的閱讀,希望這篇文章能給到你幫助和啟發(fā)。

          覺得有用就點(diǎn)個(gè)贊吧,你的點(diǎn)贊是我創(chuàng)作的最大動(dòng)力~

          我是一個(gè)努力讓大家記住的程序員。我們下期再見!!!

          能力有限,如果有什么錯(cuò)誤或者不當(dāng)之處,請(qǐng)大家批評(píng)指正,一起學(xué)習(xí)交流!


          瀏覽 42
          點(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>
                  三级片网站在线观看 | 国产欧美一区二区精品性色超碰 | 大香蕉免费亚洲美国 | 久热最新视频在线观看 | AV黄网 |