溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

為什么不要在新代碼中使用原生態(tài)類型

發(fā)布時間:2021-10-26 16:22:15 來源:億速云 閱讀:126 作者:iii 欄目:編程語言

這篇文章主要介紹“為什么不要在新代碼中使用原生態(tài)類型”,在日常操作中,相信很多人在為什么不要在新代碼中使用原生態(tài)類型問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”為什么不要在新代碼中使用原生態(tài)類型”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

Java1.5 發(fā)行版增加了泛型(Generic)。
泛型出現(xiàn)前,集合讀取的每個對象都必須進(jìn)行轉(zhuǎn)換,如果不小心插入類型錯誤對的對象,運行時的轉(zhuǎn)換處理會報錯。
泛型出現(xiàn)后,我們通過泛型可以告訴編譯器每個集合可以接受哪些對象類型,讓編譯器自動為集合的元素插入進(jìn)行轉(zhuǎn)化,并且在編譯時告知我們是否插入了類型錯誤的對象。

一些泛型的專業(yè)術(shù)語       

泛型類或泛型接口:聲明中具有一個或多個類型參數(shù)(type parameter)的類或者接口,統(tǒng)稱為泛型。eg,jdk1.5之后,List 接口只有單個類型參數(shù)E,表示列表的元素類型,所以他的接口名稱應(yīng)該是List<E>,但是人們常常把它簡稱為List。

參數(shù)化的類型(parameterized type),構(gòu)成格式是:類或接口的名稱 + 尖括號(<>)將泛型形式參數(shù)的實際類型參數(shù)列表括起來。

每個泛型都定義類一個 原生態(tài)類型(raw type),即不帶任何實際類型參數(shù)的泛型名稱。eg,List<E> 對應(yīng)的原生態(tài)類型是List。原生態(tài)類型就相當(dāng)于從類型聲明中刪除了泛型信息。

為什么不要在新代碼中使用原生態(tài)類型          

泛型:編譯期及早發(fā)現(xiàn)錯誤 

使用泛型進(jìn)行編碼,有兩個好處:

  • 優(yōu)點1:讓編寫代碼時在編譯期及早發(fā)現(xiàn)錯誤,并且助于定位報錯位置
  • 優(yōu)點2:集合使用泛型,從集合中遍歷元素時不需要再進(jìn)行手工轉(zhuǎn)換了           (編譯器替我們完成隱式轉(zhuǎn)換,并確保過程不會失敗,無論我們使用的是否for-each循環(huán))


下面我們通過一個例子闡述清楚,代碼如下:

  /**   * @exception ClassCastException   */  private static void testGenericeBeforejdk5() {    Collection stamps = new ArrayList();    stamps.add(new Coin());    for (Iterator i = stamps.iterator(); i.hasNext();){      Stamp next = (Stamp)i.next(); //ClassCastException    }  }  private static void testGenericeAfterjdk5() {    Collection<Stamp> stamps = new ArrayList();    stamps.add(new Coin());//編譯器告訴我們錯誤  }    // 兩個測試內(nèi)部類  static class Stamp{}  static class Coin{}
  • testGenericeBeforejdk5()方法里,我們希望stamps集合只會存放Stamp 類元素,但是編碼時還是不小心把一個coin放進(jìn)了這個集合。那么程序是不會在編譯時告訴程序員這個問題的,而是等到代碼真正執(zhí)行時,出現(xiàn)了異常。
Exception in thread "main" java.lang.ClassCastException: effectivejava.no23.TestGeneric$Coin cannot be cast to effectivejava.no23.TestGeneric$Stamp  at effectivejava.no23.TestGeneric.testGenericeBeforejdk5(TestGeneric.java:26)  at effectivejava.no23.TestGeneric.main(TestGeneric.java:14)

  • testGenericeAfterjdk5()方法里,我們使用了泛型定義了集合的參數(shù)類型。通過這條聲明,編譯器知道 stamps 集合應(yīng)該只包含Stamp 實例,并給以保證。因此在代碼開發(fā)時,我們不小心將一個coin 實例放進(jìn)stamps集合時,編譯器會及時提醒我們并產(chǎn)生一條編譯錯誤信息,準(zhǔn)確告知程序員哪里出現(xiàn)錯誤。

Error:(20, 28) java: 不兼容的類型: effectivejava.no23.TestGeneric.Coin無法轉(zhuǎn)換為effectivejava.no23.TestGeneric.Stamp
  • 通過比較,我們還能發(fā)現(xiàn),集合使用泛型,從集合中遍歷元素時不需要再進(jìn)行手工轉(zhuǎn)換了。

原生類型與泛型類型的區(qū)別          

其一、使用原生態(tài)類型,會失掉泛型在安全性和其他表述性方面的優(yōu)勢。

為什么繼續(xù)允許使用原生態(tài)類型呢?Java 平臺發(fā)展至今,已經(jīng)存在大量的沒有使用泛型的Java 代碼了,人們認(rèn)為讓所有這些代碼保持合法,且能夠與泛型的代碼互用,為了這個“移植兼容性”(Migration Compatibility)需求,促成了支持原生態(tài)類型的決定。         

其二、原生態(tài)類型List 和 參數(shù)化類型List<Object>有區(qū)別。

  • 原生態(tài)類型List,逃避了泛型檢查,List<Object>則明確告知編譯器:它能夠持有任意類型的對象。eg,List<String>可以傳遞給List,但不能傳遞給 List<Object>。
  • 泛型有子類型化(subtyping)的規(guī)則。List<String> 是原生態(tài)類型List 的一個子類型,但不是List<Object> 的子類型。

下面通過一個例子解讀兩者的區(qū)別:

  private static void testSubTyping() {    List<String> strings = new ArrayList<String>();    unsafeAdd(strings, new Integer(110));    String s = strings.get(0); // exception while run the method;  }    /**   * 方法使用了原生態(tài)類型,所以可以編譯。   */  private static void unsafeAdd(List list, Object o) {    list.add(0);  }    /**   * 方法使用了List<Object>替代原生態(tài)類型,所以編譯不會通過。   */  private static void unsafeAddV2(List<Object> list, Object o) {    list.add(0);  }


結(jié)論:使用List 這樣的原生態(tài)類型會丟掉類型安全性,但是使用List<Object> 這樣的參數(shù)化類型則不會。

泛型的不推薦使用場景

不要在新代碼中使用原生態(tài)類型,這條規(guī)則有兩個小小的例外,原因是:泛型信息可以在運行時被編譯器擦除了。

  • 在類文字(class literal)中必須使用原生態(tài)類型,規(guī)范不允許使用參數(shù)化類型(但允許數(shù)組類型和基本類型)[JLS,15.8.2]

ClassLiteral: 

    TypeName {[ ]} . class 

    NumericType {[ ]} . class 

    boolean {[ ]} . class 

    void . class


class literal is an expression consisting of the name of a class, interface, array, or primitive type, or the pseudo-type void, followed by a '.' and the token class.

https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.8.2          
  • instanceof 操作符對“無限制通配符”的參數(shù)化類型是無效非法的。,由于泛型信息在運行中被擦除了,這種情況下,尖括號(<>)和問號(?)顯得多余了。          
  private static void testInstanceOfInvalidOnGeneric(Object o) {    if (o instanceof Set){      //一旦確定o是個Set,必須轉(zhuǎn)換為通配符類型Set<?>,而不是原生態(tài)類型Set。這樣能避免后續(xù)代碼出現(xiàn)運行時異常。      Set<?> set = (Set<?>) o;    }  }
為什么不要在新代碼中使用原生態(tài)類型        
總結(jié)



       
涉及到的術(shù)語表        
術(shù)語示例所在條目
參數(shù)化的類型List<String>23
實際類型參數(shù)String23
泛型
List<E>
23
形式類型參數(shù)
E
23

無限制通配符類型參數(shù)
List<?>
23
原生態(tài)類型參數(shù)
List
23
有限制類型參數(shù)
List<E extends Number>
26
遞歸類型限制
List<T extends Comparable<T>>
27
有限制通配符類型參數(shù)
List<? extends Number>
28
泛型方法
static <E> List<E> asList(E[] a)
27
類型令牌
String.class
29

到此,關(guān)于“為什么不要在新代碼中使用原生態(tài)類型”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI