閱讀經典——《Effective Java》08
異構容器是指能夠容納不同類型對象的容器。像我們通常用的List
、Map
等容器,它們的原生態類型本身就是異構容器,一旦給它們設置了泛型參數,例如List<String>
、Map<Integer, String>
,它們就不再是異構容器。但是,原生態類型是不安全的,你無法知道從容器取出的類型到底是什么,很容易導致錯誤。因此,如何構建類型安全的異構容器就成了一個重要的話題。
- 使用
Map
實現類型安全的異構容器
- 局限性
使用Map
實現類型安全的異構容器
我們將要實現一個Favorites類,用來對每個類型保存一個最喜歡的實例。它的API如下:
public class Favorites {
public <T> void putFavorite(Class<T> type, T instance);
public <T> T getFavorite(Class<T> type);
}
下面是一個測試程序,說明了如何使用Favorites類保存、獲取并打印最喜愛的String、Integer和Class實例。
public static void main(String[] args) {
Favorites f = new Favorites();
f.putFavorite(String.class, "Java");
f.putFavorite(Integer.class, 0xcafebabe);
f.putFavorite(Class.class, Favorites.class);
String favoriteString = f.getFavorite(String.class);
int favoriteInteger = f.getFavorite(Integer.class);
Class<?> favoriteClass = f.getFavorite(Class.class);
System.out.printf("%s %x %s%n", favoriteString, favoriteInteger, favoriteClass.getName());
}
打印結果是
Java cafebabe Favorites
Favorite實例是類型安全的,當你向它請求String的時候,它絕不會返回一個Integer給你。同時它也是異構的,它的鍵可以是任意類型。
Favorites的實現也很簡單:
public class Favorites {
private Map<Class<?>, Object> favorites = new HashMap<Class<?>, Object>();
public <T> void putFavorite(Class<T> type, T instance) {
if (type == null)
throw new NullPointerException("Type is null");
favorites.put(type, instance);
}
public <T> T getFavorite(Class<T> type) {
return type.cast(favorites.get(type));
}
}
內部用一個Map<Class<?>, Object>
來保存所有的愛好,使用Class<?>
作為鍵記錄每個愛好的類型,而用Object
作為值不再區分它們的類型。當取出時,根據請求的類型從Map
中查找相應的值,由于值是Object
類型的,需要使用type.cast
強制轉換為type
指定的類型。只要客戶端按照API的要求使用,這里的強制轉換一定不會出錯。
局限性
這種實現方法有兩種局限性。
首先,惡意的客戶端可以破壞Favorites實例的類型安全。如果客戶端傳入原生態的Class
對象和不一致的值對象,則會在getFavorite
的cast
時拋出ClassCastException
異常。不過好在我們可以對這一情況加以約束。只需要在put
時使用一個動態的轉換就可以了:
public <T> void putFavorite(Class<T> type, T instance) {
favorites.put(type, type.cast(instance));
}
一旦客戶端傳入值類型不一致,就立即拋出異常。
第二種局限性是它不能用于泛型化類型,例如,你無法把List<String>
作為Favorites的鍵,因為List<String>.class
是個語法錯誤。這一局限性還沒有很好的解決方法。
關注作者或文集《Effective Java》,第一時間獲取最新發布文章。