Effective Java笔记第四章泛型
第七节优先考虑类型安全的异构容器
1.泛型最常用于集合,如Set和Map,以及单元素的容器,如ThreadLocal和AtomicReference。在这些用法中,它都充当被参数化了的容器。这样就限制了你每个容器只能有固定数目的类型参数。一般来说,这种情况正是我们想要的。例如:一个Set只有一个类型参数,表示他的元素类型;一个Map有两个类型参数,表示他的键和值类型。
2.有时候会需要更多的灵活性,这时候就有一种方法可以很容易的做到,就是将键进行参数化而不是将容器参数化。然后将参数化的键提交给容器,来插入或者获取值。用泛型系统来确保值的类型与他的键相符。下面我们举个例子:
public class Favorites {
private Map<Class<?>, Object> favorites = new HashMap<>();
public <T> void putFavorite(Class<T> type, T instance) {
if (null == type) {
throw new NullPointerException("Type is null");
}
favorites.put(type,type.cast(instance));
}
public <T> T getFavorite(Class<T> type) {
return type.cast(favorites.get(type));
}
public static void main(String[] args) {
Favorites favorites=new Favorites();
favorites.putFavorite(String.class,"Java");
favorites.putFavorite(Integer.class,0xcafebabe);
favorites.putFavorite(Class.class,Favorites.class);
String string = favorites.getFavorite(String.class);
Integer integer = favorites.getFavorite(Integer.class);
Class aClass = favorites.getFavorite(Class.class);
System.out.printf("%s %x %s%n",string,integer,aClass);
}
}
输出:
Java cafebabe class capter_four.day7.Favorites
1)Favorites实例是类型安全的:当你向他请求String的时候,他从不会返回一个Integer给你。同时他也是异构的(不像普通的Map,他的所有键都是不同类型的,使key-value不受类型限制)。因此,我们将Favorites 称作类型安全的异构容器。
2)注意Map< Class< ? >, Object >中的通配符类型是嵌套的:他不是属于通配符类型的Map的类型,而是Map的键,可以用来赋值,所以每个键都可以有一个不同的参数化类型。
3)value的类型不能是T,不然就指定类型了,由于不确定具体的value类型,所以用Object,之后在取值的时候对他进行了转化。
4)Class.cast方法是java转换操作符用的动态模拟。它只检验他的参数是否为Class对象所表示的类型的实例。如果是,就返回参数;否则就抛出ClassCastException异常。
Class< T >类中的源码:
@SuppressWarnings("unchecked")
public T cast(Object obj) {
if (obj != null && !isInstance(obj))
throw new ClassCastException(cannotCastMsg(obj));
return (T) obj;
}
5)Favorites类不能用在不可具体化的类型中,就是说参数Class< T > 不能传入List< String >之类的对象,因为List< String >和List< Integer >共用一个Class对象,就是List.Class。
3.总之,集合API说明了泛型的一般用法,限制你每个容器只能有固定数目的类型参数。你可以通过将类型参数放在键上而不是容器上来避开这一限制。对于这种类型安全的异构容器,可以用Class对象作为键。以这种方式使用的Class对象称作类型令牌。你也可以使用定制的键类型。