麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 學院 > 開發設計 > 正文

輕松掌握 Java 泛型 (第 2 部分)

2019-11-18 13:15:13
字體:
來源:轉載
供稿:網友

  J2SE 1.5 — 代號為“Tiger” — 計劃在 2003 年年底發布,它將包括泛型類型(如在 JSR-14 原型編譯器中預先展示的那樣,現在可下載獲得)。在第 1 部分中,我們討論了泛型類型的基礎知識,以及為什么它們是對 java 語言的一個重要且迫切需要的補充。我們還說明了為 Tiger 制定的泛型類型的實現怎么會包含數個“缺陷”,這些缺陷限制了可以使用泛型類型的上下文。
  
  為了幫助新程序員有效地使用泛型類型,我將具體說明到底泛型類型的哪些用法在 Tiger 和 JSR-14 中是被禁止的,并將說明為什么這些限制是 JSR-14(理所當然還有 Tiger)為了在 JVM 上兼容地實現泛型類型所使用的實現策略的必然結果。
  
  泛型類型的限制
  讓我們先查閱一下 Tiger 和 JSR-14 中泛型類型的使用限制:
  
  不應在靜態成員中引用封閉類型參數。
  不能用基本類型實例化泛型類型參數。
  不能在數據類型轉換或 instanceof 操作中使用“外露”類型參數。
  不能在 new 操作中使用“外露”類型參數。
  不能在類定義的 implements 或 extends 子句中使用“外露”類型參數。
  為什么會有這些限制呢?這要歸因于 Tiger 和 JSR-14 為在 JVM 上實現泛型類型所使用的機制。由于 JVM 根本不支持泛型類型,所以這些編譯器“耍了個花招”,使得似乎存在對泛型類型的支持 — 它們用泛型類型信息檢查所有的代碼,但隨即“擦除”所有的泛型類型并生成只包含普通類型的類文件。
  
  例如,將象 List< T> 這樣的泛型類型擦除得只剩下 List。“外露”類型參數 — 單獨出現而不是位于某個類型中的類型參數(如類 List< T> 中的類型參數 T)— 被簡單地擦除成它們的上界(就 T 而言,其上界就是 Object)。
  
  這一技術的功能極其強大;我們可以使幾乎所有泛型類型的精度得到增強,但又與 JVM 保持兼容。事實上,我們甚至可以交替地使用非泛型的舊類(比如 List)和其對應的泛型類(List< T>);兩者在運行時看起來是一樣的。
  
  遺憾的是,正如以上的限制所示,獲得這一功能是有代價的。以這種方式進行擦除在類型系統中引入了缺陷,這些缺陷限制我們使用泛型類型的安全性。
  
  為了幫助說明每種限制,我們查閱會出現這些限制的示例。在本文中,我們將討論前三個限制。與后兩個限制有關的問題過于復雜,因而需要更深入的研究,留待下一篇文章討論。
  
  靜態成員中的封閉類型參數
  編譯器完全禁止在靜態方法和靜態內部類中引用封閉類型參數。所以,舉例來說,以下代碼在 Tiger 中就是非法的:
  
  清單 1. 在靜態上下文中非法引用封閉類型參數 class C< T> {
   static void m() {
    T t;
   }
  
   static class D {
    C< T> t;
   }
  }
  
  當編譯這一代碼時,會生成兩個錯誤:
  
  在靜態方法 m 中非法引用 T 的錯誤
  在靜態類 D 中非法引用 T 的錯誤
  當定義靜態字段時,情況變得更加復雜。在 JSR-14 和 Tiger 中,在泛型類的所有實例中共享該類中的靜態字段。現在,在 JSR-14 編譯器 1.0 和 1.2 中,假如您在靜態字段聲明中引用類型參數,編譯器不會報錯,但它本應該這么做。字段被共享這一事實很輕易在運行時導致希奇的錯誤,如在不包含數據類型轉換的代碼中出現 ClassCastException。
  
  例如,以下程序將在這兩個版本的 JSR-14 下通過編譯而沒有任何警告:
  
  清單 2. 在靜態字段中對封閉類型參數的有問題的引用 class C< T> {
   static T member;
  
   C(T t) { member = t; }
  
   T getMember() { return member; }
  
   public static void main(String[] args) {
    C< String> c = new C< String>("test");
    System.out.PRintln(c.getMember().toString());
    new C< Integer>(new Integer(1));
    System.out.println(c.getMember().toString());
   }
  }
  
  請注重,每次分配類 C 的實例時,都要重新設置靜態字段 member。而且,它被設置成的對象類型取決于 C 的實例的類型!在所提供的 main 方法中,第一個實例 c 是 C< String> 類型。而第二個是 C< Integer> 類型。每當從 c 訪問 member 這一共享靜態字段時,總是假定 member 的類型是 String。但是,在分配了類型為 C< Integer> 的第二個實例之后,member 的類型是 Integer。
  
  運行 C 的 main 方法的結果可能會讓您吃驚 — 它將發出一個 ClassCastException!源代碼根本沒有包含任何數據類型轉換,怎么會這樣呢?事實證實編譯器確實在編譯階段將數據類型轉換插入到代碼中,這樣做是為了解決類型擦除會降低某些表達式的類型的精度這一事實。這些數據類型轉換被期望能夠成功,但在本例中卻沒有成功。
  
  應該認為 JSR-14 1.0 和 1.2 的這一非凡“功能”是個錯誤。它破壞了類型系統的健全性,或者可以說,它破壞了類型系統應該和程序員達成的“基本契約”。象對靜態方法和類所做的那樣,只要防止程序員在靜態字段中引用泛型類型,情況就會好很多。
  
  請注重答應這種有潛在“爆炸性”的代碼存在所帶來的問題并不是程序員有意在自己的代碼中覆蓋類型系統。問題是程序員可能會無意中編寫這樣的代碼(比如,由于“復制和粘貼”操作,錯誤地在字段聲明中包括靜態修飾符)。
  
  類型檢查器應該能幫助程序員從這些類型的錯誤中恢復,但對于靜態字段而言,類型系統實際上會使程序員更迷惑。當未使用數據類型轉換的代碼中顯示的唯一錯誤就是 ClassCastException 時,我們應如何診斷這樣的錯誤?對于不清楚 Tiger 中泛型類型所用的實現方案而又恰好假定類型系統合理運行的程序員而言,情況更糟。因為在這樣的情況下,類型系統不是合理地運行。
  
  幸運的是,JSR-14 的最新版本(1.3)公布在靜態字段中使用類型參數是不合法的。因此,我們有理由期待在 Tiger 的靜態字段中使用類型參數也是不合法的。
  
  泛型類型參數和基本類型
  和我們剛才討論的不同,這一限制沒有同樣的潛在缺陷,但它會使您的代碼非常冗長。例如,在 java.util.Hashtable 的泛型版本中,有兩種類型參數:用于 Key 類型的和用于 Value 類型的。因此,假如我們想要一個將 String 映射到 String 的 Hashtable,我們可以用表達式 new Hashtable< String, String>() 指定新的實例。但是,假如我們想要一個將 String 映射到 int 的 Hashtable,我們只能創建 Hashtable< String, Integer> 的實例,并將所有的 int 值包裝在 Integer 中。
  
  同樣,Tiger 在這方面當然也是由所用的實現方案得到的。既然類型參數被擦除為它們的界限,而界限不能是基本類型,所以一旦類型被擦除,則對基本類型的實例化會完全沒有意義。
  
  數據類型轉換或 instanceof 操作中的“外露”參數
  回想一下,對于“外露”類型參數,我們是指在詞匯上單獨出現的類型參數,而不是更大類型的語法子組件。例如,C< T> 不是“外露”類型參數,但(在 C 主體中)T 是。
  
  假如在代碼中對“外露”類型參數進行數據類型轉換或 instanceof 操作,則編譯器將發出名為“unchecked”的警告。例如,以下代碼將生成警告:Warning: unchecked cast to type T:
  
  清單 3. 帶 unchecked 操作的泛型代碼 import java.util.Hashtable;
  interface Registry {
   public void register(Object o);
  }
  class C< T> implements Registry {
   int counter = 0;
   Hashtable< Integer, T> values;
  
   public C() {
    values = new Hashtable< Integer, T>();
   }
  
   public void register(Object o) {
    values.put(new Integer(counter), (T)o);
    counter++;
   }
  }
  
  您應該嚴厲地對待這些警告,因為它們說明您的代碼在運行時會表現得非常希奇。事實上,它們會使得診斷代碼變得極為困難。在以前的代碼中,我們認為假如對實例 C< JFrame> 調用 register("test"),會發出 ClassCastException。但并非如此;計算將繼續,就仿佛數據類型轉換成功了一樣,然后在進一步進行計算時發出錯誤,或者更糟:用遭破壞的數據完成計算,但不向外發出任何錯誤信號。同樣,對“外露”類型參數的 instanceof 檢查將在編譯時產生“unchecked”警告,而且檢查將不會如期在運行時進行。
  
  雙刃劍
  那么,這里到底發生了什么?因為 Tiger 依靠類型擦除,所以數據類型轉換和 instanceof 測試中的外露類型參數被“擦除”為它們的上界(在前面的例子中,那將是類型 Object)。因此,對類型參數的數據類型轉換將變成對參數上界的轉換。
  
  同樣,instanceof 將檢查操作數是否是參數界限的 instanceof。那根本不是我們打算做的,假如是的話,我們完全可以顯式地強制轉換為界限。因此,通常應避免對類型參數使用數據類型轉換和 instanceof 檢查。
  
  然而,有時為了編譯代碼,您必須依靠對類型參數的數據類型轉換。假如是這樣的情況,只要記住,在代碼的那一部分中,類型檢查不保險 — 要靠自己。
  
  盡管泛型類型是制作健壯代碼的強大武器,但我們已經演示了誤用它們會使代碼不再健壯而且極難診斷和修正。下次,我們將介紹 Tiger 中泛型類型的后兩個限制,并討論試圖在泛型 Java 類型系統中包括它們時必定會出現的一些問題。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 亚洲啪 | 日韩视频在线视频 | 超碰一区| 国产妇女乱码一区二区三区 | 日本不卡视频在线观看 | 激情视频导航 | 日本在线视频一区二区三区 | 国产 视频 一区二区 | 精品亚洲夜色av98在线观看 | 欧美成人精品一区 | 少妇一级淫片免费看 | 国产精品视频二区不卡 | 国产精品99久久久久久久女警 | 久久久久久久久久久久久久久伊免 | 日韩欧美色综合 | 久久一本日日摸夜夜添 | 国产毛片在线看 | 久久久精品视频免费 | 日本爽快片100色毛片视频 | 精品国产视频一区二区三区 | 久久久久久69 | 黄色成人小视频 | 色av综合在线 | 免费国产人成网站 | 精品国产1区2区3区 av视屏 | 国产精品99久久久久久久vr | 福利在线免费视频 | 极品xxxx欧美一区二区 | 欧美一级一区二区三区 | 欧美日本综合 | 国产剧情在线观看一区二区 | 国产做爰全免费的视频黑人 | 欧美精品一区二区三区在线 | 美女黄页网站免费进入 | 久久精品视频亚洲 | 亚洲xxx视频 | 暴力肉体进入hdxxxx古装 | 黄污网址| 国产一区二区三区精品在线观看 | 久久国产28 | 色播久久|