閱讀經(jīng)典——《Effective Java》08
異構(gòu)容器是指能夠容納不同類型對象的容器。像我們通常用的List
锨亏、Map
等容器,它們的原生態(tài)類型本身就是異構(gòu)容器器予,一旦給它們設(shè)置了泛型參數(shù)劣摇,例如List<String>
弓乙、Map<Integer, String>
,它們就不再是異構(gòu)容器勾习。但是懈玻,原生態(tài)類型是不安全的,你無法知道從容器取出的類型到底是什么艺栈,很容易導(dǎo)致錯誤。因此湿右,如何構(gòu)建類型安全的異構(gòu)容器就成了一個重要的話題毅人。
- 使用
Map
實(shí)現(xiàn)類型安全的異構(gòu)容器
- 局限性
使用Map
實(shí)現(xiàn)類型安全的異構(gòu)容器
我們將要實(shí)現(xiàn)一個Favorites類尖殃,用來對每個類型保存一個最喜歡的實(shí)例送丰。它的API如下:
public class Favorites {
public <T> void putFavorite(Class<T> type, T instance);
public <T> T getFavorite(Class<T> type);
}
下面是一個測試程序,說明了如何使用Favorites類保存俐载、獲取并打印最喜愛的String、Integer和Class實(shí)例科乎。
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());
}
打印結(jié)果是
Java cafebabe Favorites
Favorite實(shí)例是類型安全的茅茂,當(dāng)你向它請求String的時候空闲,它絕不會返回一個Integer給你走敌。同時它也是異構(gòu)的掉丽,它的鍵可以是任意類型。
Favorites的實(shí)現(xiàn)也很簡單:
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));
}
}
內(nèi)部用一個Map<Class<?>, Object>
來保存所有的愛好僧须,使用Class<?>
作為鍵記錄每個愛好的類型担平,而用Object
作為值不再區(qū)分它們的類型暂论。當(dāng)取出時拌禾,根據(jù)請求的類型從Map
中查找相應(yīng)的值,由于值是Object
類型的扼菠,需要使用type.cast
強(qiáng)制轉(zhuǎn)換為type
指定的類型坝咐。只要客戶端按照API的要求使用墨坚,這里的強(qiáng)制轉(zhuǎn)換一定不會出錯。
局限性
這種實(shí)現(xiàn)方法有兩種局限性盗尸。
首先帽撑,惡意的客戶端可以破壞Favorites實(shí)例的類型安全。如果客戶端傳入原生態(tài)的Class
對象和不一致的值對象亏拉,則會在getFavorite
的cast
時拋出ClassCastException
異常及塘。不過好在我們可以對這一情況加以約束笙僚。只需要在put
時使用一個動態(tài)的轉(zhuǎn)換就可以了:
public <T> void putFavorite(Class<T> type, T instance) {
favorites.put(type, type.cast(instance));
}
一旦客戶端傳入值類型不一致,就立即拋出異常肋层。
第二種局限性是它不能用于泛型化類型栋猖,例如掂铐,你無法把List<String>
作為Favorites的鍵,因?yàn)?code>List<String>.class是個語法錯誤爆班。這一局限性還沒有很好的解決方法柿菩。
關(guān)注作者或文集《Effective Java》雨涛,第一時間獲取最新發(fā)布文章枢舶。