泛型方法的反模式
我承認,我自己也忍不住用過這項技術。它簡直太方便了,可以省去一次不必要的型別轉化。這就是:
interface SomeWrapper { <T> T get(); }
現在你便可以安全地將包裝類轉換成任意型別了:
SomeWrapper wrapper = ... // Obviously Object a = wrapper.get(); // Well... Number b = wrapper.get(); // Risky String[][] c = wrapper.get(); // Unprobable javax.persistence.SqlResultSetMapping d = wrapper.get();
這也正是你在使用時所用到的API,jOOR是我們開發並開源的一個反射工具庫,它可以提升我們編寫整合測試的效率。有了jOOR,你可以這麼編寫程式碼:
Employee[] employees = on(department) .call("getEmployees").get(); for (Employee employee : employees) { Street street = on(employee) .call("getAddress") .call("getStreet") .get(); System.out.println(street); }
這個API非常簡單。on()方法會對某個物件或類進行封裝。call()方法會通過反射去呼叫這個物件上的方法(不需要簽名正確,也不需要方法宣告成public,同時也不會丟擲任何的受檢查異常)。同時你也不需要強制型別轉換,便能通過get()方法將結果轉換成任意的型別。
對於一款像jOOR這樣的反射庫而言,這麼做是沒問題的,因為這個庫本身就不是型別安全的。當然不會安全了,因為本來就是反射。
不過這還是會讓人感到有些不爽。感覺就是在呼叫點這裡承諾會返回某個型別,但實際上卻無法兌現,這很可能會導致ClassCastException異常——在有了Java 5以及泛型之後才開始寫Java的開發人員是很難體會到這種感覺的。
但JDK也是這麼做的。。
沒錯,JDK是這麼幹了。不過這樣的情況並不多,並且只是在泛型引數的型別並不那麼重要的情況下才會這麼做。比如說,當你通過Collection.emptyList()獲取一個空列表的時候,這個方法的實現是這樣的:
@SuppressWarnings("unchecked") public static final <T> List<T> emptyList() { return (List<T>) EMPTY_LIST; }
沒錯,EMPTY_LIST從List強轉成List是挺不安全的。但從語義層面來說,這樣的強轉是安全的。你不能修改這個列表,因為這是個空列表。List中也不會有任何一個方法會返回給你一個非目標型別的T或者T[]的例項。因此,這些做法都是合法的:
// perfectly fine List<?> a = emptyList(); // yep List<Object> b = emptyList(); // alright List<Number> c = emptyList(); // no problem List<String[][]> d = emptyList(); // if you must List<javax.persistence.SqlResultSetMapping> e = emptyList();
JDK的開發人員從來都會非常小心地不去對你所可能獲取到的泛型型別做出任何無法兌現的承諾。也就是說,即便存在一個更適合的型別的時候,通常返回給你的也都是Object型別。
哪個型別更適合只有你知道,編譯器可不瞭解。型別擦除是有代價的,代價就是當包裝類或者集合為空的時候。像這樣的一個表示式編譯器是無法得知它的型別的,它可不能不懂裝懂。也就是說:
不要使用這種只為了省一次型別轉化的泛型方法的反模式。
相關文章
- 泛型類和泛型方法泛型
- java泛型之泛型方法。Java泛型
- 泛型類、泛型方法及泛型應用泛型
- Java泛型知識點:泛型類、泛型介面和泛型方法Java泛型
- 泛型類、泛型方法、型別萬用字元的使用泛型型別字元
- Java中的泛型方法Java泛型
- 【譯】在非泛型類中建立泛型方法泛型
- Scala 泛型型別和方法泛型型別
- C# 泛型方法C#泛型
- 泛型轉DataTable方法泛型
- Java 中的泛型方法及 FunctionJava泛型Function
- 泛型--泛型萬用字元和泛型的上下限泛型字元
- 菜鳥譯文(二)——使用Java泛型構造模板方法模式Java泛型模式
- 應用最廣泛的模式——工廠方法模式模式
- 集合框架-泛型方法的概述和使用框架泛型
- java靜態方法使用泛型Java泛型
- Java基礎之泛型方法Java泛型
- 泛型方法、初始集合和集合的遍歷泛型
- Java反射—方法的反射、深入瞭解泛型Java反射泛型
- 【java】【泛型】泛型geneticJava泛型
- Scala的泛型泛型
- TypeScript 泛型介面和泛型類TypeScript泛型
- Go 泛型之泛型約束Go泛型
- java泛型之泛型陣列。Java泛型陣列
- 泛型泛型
- 泛型類及系統中常用的泛型類泛型
- 泛型最佳實踐:Go泛型設計者教你如何用泛型泛型Go
- 泛型物件的使用泛型物件
- 十、GO程式設計模式 : 泛型程式設計Go程式設計設計模式泛型
- Java的Void方法是反模式的? - DZoneJava模式
- Gson通過藉助TypeToken獲取泛型引數的型別的方法泛型型別
- 型別 VS 泛型型別泛型
- TypeScript 泛型型別TypeScript泛型型別
- Java泛型(類、介面、方法)及萬用字元Java泛型字元
- 泛型(一)泛型
- 泛型(三)泛型
- 泛型(二)泛型
- 泛型(四)泛型