EffectiveJava(29)优先考虑类型安全的异构容器

当你的泛型集合需要更多的灵活性,你可以将键进行参数化而不是将容器进行参数化.然后将参数化的键提交给容器,来插入或者获取值.用泛型系统来确保值得类型与它的键相符.
我们创建一个Favorite类来模拟这种情况

    public class Favorites {
        //不像普通的Map,它的所有键都是不同类型的.(异构)
        private Map<Class<?>, Object> favoties = new HashMap<Class<?>, Object>();
        //把从指定的Class对象到指定favorite实例的一个映射放到favorites中
        public <T> void putFavorite(Class<T> type, T instance) {
            if (type == null) {
                throw new NullPointerException("Type is Null");
            }
            favoties.put(type, instance);
        }
        //从favorites映射中获得预指定Class对象相对应的值.
        public <T> T getFavorite(Class<T> type) {
            return type.cast(favoties.get(type));
        }
    }
在这个类中,每一个Favorites实例都得到一个称作favoties的私有的Map<Class<?>,Object>支持.但是它不属于通配符类型的Map的类型,而是他的键类型.所以,每个键都可以有一个不同的参数化类型.而且Favorites中的Map不能保证每个值的类型都与键的类型相同.
我们测试一下编写的这个类来看我们描述的它的特点
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 favoriteStr = favorites.getFavorite(String.class);
        int favoriteInteger = favorites.getFavorite(Integer.class);
        Class<?> cls = favorites.getFavorite(Class.class);
        System.out.printf("%s  %x %s%n",favoriteStr,favoriteInteger,cls.getName());
    }
    它的输出依次为JAVA  cafebabe com.generic.syscontainer.Favorites
同样的,Favorites也有局限性:
    1.恶意的客户端可以很轻松地破坏Favorites实例的类型安全,只要以它的原生态形式
    解决方案:让puFavorite方法检验instance是否真的是type所表示的类型的实例
    favoties.put(type, instance); ----->>>>favorites.put(type,type.cast(instance));
    2.他不能用在不可具体化的类型中.因为List<String>.Class等是个错误语法,它们共用List.Class一个对象
    解决方案:并没有很好地解决方案,可以通过一种加super type token的方法优化,但他本身也有局限性

但是,Favorites使用的类型令牌是无限制的:getFavorite和putFavorite接受任何Class对象
    ---->>>>
    public <T extends Annotation> T getAnnotation(Class<T> annotationType);
    参数annotationType是一个表示注解类型的有限制的类型令牌.
    接下来如果你想把Class<?>的对象传给一个需要有限制的类型令牌的方法,将对象转换成Class<? extends Annotation>是非受检的,会产生编译警告.要安全的避开这条警告,可以通过将调用它的Class对象转换成用其参数表示的类的一个子类,称作asSubclass
            static Annotation getAnnotation(AnnotationElement element,String annotationTypeName){
                Class<?> annotationType = null;
                try{
                    annotationType = Class.forName(annotationTypeName);
                }catch(Exception e){
                    throw new IllegalArgumentException(e);
                }
                return element.getAnnotation(annotationType.asSubclass(Annotation.class));
            }

总结:集合API说明了泛型的一般用法,限制你每个容器只能有固定数目的类型参数.你可以通过将类型参数放在键上而不是容器上来避开这一条限制.对于这种类型安全的异构容器,可以用Class对象作为键.以这种方式实用的Class对象称作类型令牌.你也可以使用定制的键类型.例如,用一个DatabaseRow类型表示一个数据库行(容器),用泛型Column作为他的键

原文地址:https://www.cnblogs.com/qwop/p/6637276.html