Java高品質代碼之 — 泛型與反射

來源:互聯網
上載者:User

標籤:

在Java5後推出了泛型,使我們在編譯期間操作集合或類時更加的安全,更方便代碼的閱讀,而讓身為編譯性語言的Java提供動態性的反射技術,更是在架構開發中大行其道,從而讓Java活起來,下面看一下在使用泛型和反射需要注意和瞭解的事情?


1.Java的泛型是類型擦除的
????? Java中的泛型是在編譯期間有效,在運行期間將會被刪除,也就是所有泛型參數類型在編譯後都會被清除掉.請看以下例子?

Java代碼???

  1. publicstaticvoid?test(List??testParameter)?{??
  2. ?
  3. }??
  4. ?
  5. publicstaticvoid?test(List??testParameter)?{??
  6. ?
  7. }??



????? 以上是一個最最簡單的重載例子,在編譯後泛型型別是會被擦除的,所以無論是List?或List?都會變成List?,所以參數相同,重載不成立,無法編譯通過,而且讀者可以嘗試將不同泛型的類getClass()看看,他們的Class是一樣的??


2.不能初始化泛型參數和數組
????? 請觀察以下代碼?

Java代碼???

  1. class?Test??{??
  2. //編譯不通過
  3. private?T?t?=?new?T();??
  4. //編譯不通過
  5. private?T[]?tArray?=?new?T[5];??
  6. //編譯通過
  7. private?List??list?=??new?ArrayList?();??
  8. }??



????? Java語言是一門強型別,編譯型的安全語言,它需要確保運行期的穩定性和安全性,所以在編譯時間編譯器需要嚴格的檢查我們所聲明的類型,在編譯期間編譯器需要擷取T類型,但泛型在編譯期間已經被擦除了,所以new T()和new T[5]都會出現編譯錯誤,而為什麼ArrayList卻能成功初始化?這是因為ArrayList表面是泛型,但在編譯期間已經由ArrayList內部轉型成為了Object,有興趣的讀者可以看一下ArrayList的源碼,源碼中容納元素的是private transient Object[] elementData,是Object類型的數組?


3.強制聲明泛型的實際類型
????? 在使用泛型時,在大多數情況下應該聲明泛型的類型,例如該集合是存放User對象的,就應該使用List?來聲明,這樣可以盡量減少類型的轉換,若只使用List而不聲明泛型,則該集合等同於List?

4.注意泛型沒有繼承說
????? 請看以下例子?

Java代碼???

  1. publicstaticvoid?main(String[]?args)?{??
  2. //?編譯不通過
  3. ????List
  4. //?or
  5. ????List
  6. }??



????? 上面代碼的商務邏輯為有一組元素,但我不確定元素時整形還是浮點型,當我在確定後建立對應的泛型實作類別,剛接觸泛型的讀者有可能會犯以上錯誤,理解為泛型之間是可以繼承的,例如聲明Object的泛型實際上建立Integer泛型,錯誤認為泛型之間也存在繼承關係,但這是不正確的,泛型是協助開發人員編譯期間類型檢查安全.我們可以換種方式實現以上商務邏輯?

Java代碼???

  1. publicstaticvoid?main(String[]?args)?{??
  2. //Number?為Integer?和?Double?的公用父類
  3. ????List??numberList?=??new?ArrayList?();??
  4. ?
  5. //使用萬用字元,代表實際類型是Number類型的子類
  6. ????Listextends?Number>?numberList2?=?new?ArrayList?();??
  7. //or
  8. ????Listextends?Number>?numberList3?=?new?ArrayList?();??
  9. }??



5.不同情境使用不同的萬用字元
???? 泛型當中支援萬用字元,可以單獨使用 ‘?‘ 來表示任意類型,也可以使用剛才上面例子中的extends關鍵字表示傳入參數是某一個類或介面的子類,也可以使用super關鍵字表示接受某一個類型的父類型,extends和super的寫法一樣,extends是限定上界,super為限定下界?


6.建議採用順序為List?List List?
????? 以上三者都可以容納所有的對象,但使用的順序應該是首選List?,然後List,最後才使用List?

7.使用多重邊界限定
????? 現在有一個業務需求,收錢時需要是我們本公司的員工(繼承User),同時亦需要具備收銀員的功能(實現Cashier),此時我們當然可以想到接受1個User然後判斷User是否實現了Cashier介面,但在泛型的世界中,我們可以怎麼做?請看以下例子?

Java代碼???

  1. publicstatic???extends?User?&?Cashier>??void?CollectMoney(T?t){??
  2. ?
  3. }??



????? 以上例子就表明,限定了上界,只要是User和Cashier的子類才可以作為參數傳遞到方法當中?



======================我是泛型和反射的分界線====================


8.注意Class類的特殊性
????? Java語言是先把Java源檔案編譯成尾碼為class的位元組碼檔案,然後通過ClassLoader機制把類檔案載入到記憶體當中,最後產生執行個體執行的,在描述一個類是,Java使用了一個元類來對類進行描述,這就是Class類,他是一個描述類的類,所以註定Class類是特殊的?
????? 1.Class類無建構函式,Class對象載入時由JVM通過調用類載入器的?
??????? defineClass方法來構造Class對象?

????? 2.Class類還可以描述基礎資料型別 (Elementary Data Type),由於基本類型並不是Java中的對象,它們?
??????? 一般存在於棧,但Class仍然可以對它們進行描述,例如使用int.class?

????? 3.其對象都是單例模式,一個Class的實現對象描述一個類,並且只描述一個類?
??????? 所以只要是該被描述的類所有對象都只有一個Class執行個體?

????? 4.Class類是Java反射的入口?


9.適時選擇getDeclaredXXX和getXXX
????? Class類中提供了很多getDeclaredXXX和getXXX的方法,請看以下例子?

Java代碼???

  1. publicstaticvoid?main(String[]?args)?{??
  2. ????Class??cls?=?User.?class;??
  3. //擷取類方法
  4. ????cls.getDeclaredMethods();??
  5. ????cls.getMethods();??
  6. //擷取類建構函式
  7. ????cls.getDeclaredConstructors();??
  8. ????cls.getConstructors();??
  9. //擷取類屬性
  10. ????cls.getDeclaredFields();??
  11. ????cls.getFields();??
  12. }??



????? getXXX的方式是擷取所有公用的(public)層級的,包括從父類繼承的方法,而getDeclaredXXX的方式是擷取所有的,包括公用的(public),私人的(private),不受限與存取權限,?


10.反射訪問屬性或方法時將Accessible設定為true
????? 在調用建構函式或方法的invoke前檢查accessible已經是公認的寫法,例如以下代碼?

Java代碼???

  1. publicstaticvoid?main(String[]?args)?throws?Exception?{??
  2. ????Class??cls?=?User.?class;??
  3. //建立對象
  4. ????User?user?=?cls.newInstance();??
  5. ?
  6. //擷取test方法
  7. ????Method?method?=?cls.getDeclaredMethod("test");??
  8. ?
  9. //檢查Accessible屬性
  10. if(!method.isAccessible()){??
  11. ????????method.setAccessible(true);??
  12. ????}??
  13. ????method.invoke(user);??
  14. }??



????? 讀者可以嘗試擷取Class的getMethod,也就是公開的方法,再輸出isAccessible,可以看到輸出的其實也是false,其實因為accessible屬性的語義並不是我們理解的存取權限,而是指是否進行安全檢查,而安全監察是非常消耗資源的,所以反射提供了Accessible可選項,讓開發人員逃避安全檢查,有興趣的讀者可以查看AccessibleObject類觀察其源碼瞭解安全檢查.?

11.使用forName動態載入類
????? forName相信各位讀者不會陌生,在使用JDBC時要動態載入資料庫驅動就是使用forName的方式進行載入,同時亦可以從外部設定檔中讀取類的全路徑字串進行載入,在使用forName時,被載入的類就會被載入到記憶體當中,只會載入類,並不會執行任何代碼,而我們的資料庫驅動就是利用static代碼塊來執行操作的,因為當類被載入到記憶體中時,會執行static代碼塊?


12.使用反射讓模板方法更強大
????? 模板方法的定義是,定義一個操作的演算法骨架,將某些步驟延遲到子類當中實現,而實現細節由子類決定,父類只決定骨架,以下是一個傳統模板方法的案例?

Java代碼???

  1. publicabstractclass?Test?{??
  2. ?
  3. publicfinalvoid?doSomething()?{??
  4. ????????System.out.println("start...");??
  5. ????????doInit();??
  6. ????????System.out.println("end.....");??
  7. ????}??
  8. ?
  9. protectedabstractvoid?doInit();??
  10. }??



??? 此時子類只需要繼承Test類實現doInit()方法即可嵌入到doSomething中,現在我們有一個需求,若我在doSomething中需要調用一系列的方法才能完成doSomething呢?而且我調用方法的數量並不確定,只需遵從某些規則則可將方法添加到doSomething方法當中.請看以下代碼?

Java代碼???

  1. publicabstractclass?Test?{??
  2. ?
  3. publicfinalvoid?doSomething()?throws?Exception?{??
  4. ?
  5. ????????Method[]?methods?=?this.getClass().getDeclaredMethods();??
  6. ????????System.out.println("start...");??
  7. for?(Method?method?:?methods)?{??
  8. if?(this.checkInitMethod(method))?{??
  9. ????????????????method.invoke(this);??
  10. ????????????}??
  11. ????????}??
  12. ????????System.out.println("end.....");??
  13. ????}??
  14. ?
  15. privateboolean?checkInitMethod(Method?method)?{??
  16. //?方法名初始是否為init
  17. return?method.getName().startsWith("init")??
  18. //?是否為public修飾
  19. ????????????????&&?Modifier.isPublic(method.getModifiers())??
  20. //?傳回值是否為void
  21. ????????????????&&?method.getReturnType().equals(Void.TYPE)??
  22. //?是否沒有參數
  23. ????????????????&&?!method.isVarArgs()??
  24. //?是否抽象類別型
  25. ????????????????&&?!Modifier.isAbstract(method.getModifiers());??
  26. ????}??
  27. }??



????? 看到上面的代碼,讀者是否有似曾相識的感覺?在使用Junit3時是不是只要遵守方法的簽名約定,就能成為測試類別?使用這種反射可以讓模板方法更強大,下次需要使用多個方法在模板方法中時,不要建立多個抽象方法,嘗試使用以上方式?


13.不要過分關注反射的效率
????? 反射的效率是一個老生常談的問題,普通的調用方法,建立類,在反射的情況下需要調用諸多API才能實現,效率當然要比普通情況下低下,但在項目當中真正引起效能問題的地方,絕大數不會是由反射引起的,而反射帶給我們的卻是如此的美妙,當今的各系列開源架構,幾乎都存在反射的身影,而且大量存在更比比皆是,讓Java這個沉睡的美女活起來的,非反射莫屬?


總結:?
????? 筆者在本文章中只從《改善Java程式的151建議》中提取部分進行歸納性敘述,推薦各位讀者購買這本書,該書不僅從案例中學習,而且涉及到原理,底層的實現,不僅告訴你應該怎麼做,還告訴你為什麼要這樣做.?

Java高品質代碼之 — 泛型與反射

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.