阅读经典——《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》,第一时间获取最新发布文章。