术语 :
有时会需要未限定固定数目的类型参数的容器,此时,可以将容器的键进行参数化而不是将容器参数化。然后将参数化的键交给容器来插入或者获得值。用泛型系统来确保值的类型和它的键相符。示例代码如下:
// Typesafe heterogeneous container pattern - implementation
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);
}
上面的代码中,class方法返回的是一个class<T>的形式。Favorites是类型安全的,它总是按照键返回正确的值,同时它也是异构的,因为它的容器的键不是同一种类型,这有别于传统的Map,因此,将Favorites称作类型安全的异构容器。异构来自哪?答案是无限制通配符的键Class<?>,在这里它仅代表是某种class,因此允许将不同类的class放入同一个Map,这就是异构的原因。注意,因为我们使用的值类型是Object,因此Map并无法保证键一对能对应正确的值,它只知道值是一个Object就可以了,这种对应的关系是实现者自己来确保的。手动重新建立类型与值之间的关系是在getFavorite方法中进行的,利用Class的cast方法,将对象动态地转换成Class对象所表示的类型。cast只是检验它的参数是不是为Class对象所表示的类型的实例。如果是,就返回参数,如果不是就抛出ClassCastException。
public class Class<T> {
T cast(Object obj);
}
Favorites类有两种局限:一是恶意用户可以通过使用原生态形式的Class来破坏年Favorites实例的类型安全。这种方式可以通知在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。如果需要限制些可以传递给方法的类型,则可以使用有限制的通配符类型。public <T extends Annotation>
T getAnnotation(Class<T> annotationType);
在这面这段代码里,如果想把一个Class<?>传递给getAnnotation方法,那么按照要求,可能想到可以将其转换成Class<? extends Annotation>,但是这种行为是非受检的,会收到编译器的警告,但是,可以利用Class类提供的一个安全且动态地执行这种转换的实例方法,asSubclass,它将调用它的Class对象转换成用其参数表示的类的一个子类型,如果转换成功,该方法就返回它的参数,如果失败则抛出ClassCastException。见如下例子:
public <T extends Annotation>
T getAnnotation(Class<T> annotationType);
// Use of asSubclass to safely cast to a bounded type token
static Annotation getAnnocation(AnnocationElement element,
String annotationTypeName) {
Class<?> annotationType = null;// Unbounded type token
try {
annotationType = Class.forName(annotationTypeName);
} catch (Exception ex) {
throw new IllegalArgumentException(ex);
}
return element.getAnnotation {
annotationType.asSubclass(Annotation.class);
}
}