泛型常用于以及一些单元素容器。但一般来说一个set只有一个类型参数,表示他的元素类型,一个Map有两个类型参数,表示键和值。
考虑这么一种情况,数据库中的字段都是不同类型,怎么将一条数据加入到容器中。我们需要一个容器能够容纳不同类型的值。
简单一想我们用Map<Object,Object>不就行了,但这样有个缺点,我们无法通过键来确定值得类型,也就是说我们取值的时候是无法获得值得类型的。还有一种想法我们是不是可以制定一个规范,让键是什么类型,值就是什么类型。但这样也有问题,我们无法确保用户可以严格按照这个规范来输入数据。
幸运的是,我们有一个方法可以轻松做到这一点。请看如下代码:
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));
}
}
// Demo: Typesafe heterogeneous container pattern - client
public static void main(String[] args) {
Favorites f = new Favorites();
f.putFavorite(String.class, "Java");
f.putFavorite(Integer.class, 0xcafebale);
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);
}
这个Favorites类允许客户端从任意多的其他类中获取最喜欢的实例,放入到容器中,最后也能正确的获取最喜欢的实例。这个例子,值得类型就是多种类型的。
将类的Class类作为键,并且使用Class<T> 能够识别 Class 是哪种类型的。类Class在Java 1.5后就被泛型化了,也就是说String.class属于Class<String>。这种传达类型的方法就叫做type token。通过这个我们首先了类型安全,也就是我们可以通过Class的类型来判断值的类型,从而返回正确的值类型。
那么异构是哪里来的呢?答案是无限制通配符的键Class<?>,在这里它仅代表是某种class,因此允许将不同类的class放入同一个Map,这就是异构的原因。因为它只是个键,所以不存在使用无限制通配符后就无妨把任何东西放到map中这个问题。
Favorites类有两种局限:一是恶意用户可以通过使用原生态形式的Class来破坏年Favorites实例的类型安全。比如利用原生态类型HashSet中添加String类型来破坏HashSet<Integer>.这种方式可以通知在putFavorite中进行类型检查来确保实例对象进行检查。
public <T> void putFavorite(Class<T> type, T instance) {
if (type == null)
throw new NullPointerException("Type is null");
favorites.put(type, type.cast(instance));
}
第二个局限性在于它不能用在不可具体化的类型中。比如说可以存储喜爱的String,String[],但是不能存储List<String>。因为 List<String>.class是语法错误。因为在运行时他们的类型会被擦除,所在List<String>与List<Integer>实际上是共用一个Class。如果需要限制些可以传递给方法的类型,则可以使用有限制的通配符类型。