java学习之—泛型(优化考虑类型安全的异构容器)

  1.泛型最常用于集合,如Set和Map,以及单元素的容器,如ThreadLocal和AtomicReference。在这些用法中,它都充当被参数化了的容器。

  2.例如,数据库行可以有任意多的列,如果能以类型安全的方式访问所有列就好了。幸运的是,有一种方法可以很容易地做到这一点。这种想法就是将键(key)进行参数化而不是将容器(container)参数化。然后将参数化的键提交给容器,来插入或者获取值。用泛型系统来确保值的类型与它的键相符。

  3.Class对象充当参数化键的部分。之所以可以这样,是因为类Class在Java 1.5版本中被泛型化了。类的类型从字面上来看不再只是简单的Class,而是Class<T>。例如,String.class属于Class<String>类型,Integer.class属于Class<Integer>类型。

  4.Favorites实例是类型安全的(typesafe)的:当你向它请求String的时候,它从来不会返回一个Integer给你。同时它也是异构的(heterogeneous):不像普通的map,它的所有键都是不同类型的。因此,我们将Favorites称作类型安全的异构容器(typesafe heterogeneous container)。

  示例:Favorites类,类似于map,除了键被参数化之外。

public class Favorites {
    // Typesafe heterogeneous container pattern - implementation
    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);
    }
//favorites.get(type)是获取map对应的值,type.cast是将值进行类型转换
public <T> T getFavorite(Class<T> type) { return type.cast(favorites.get(type)); } // Typesafe heterogeneous container pattern - client 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()); } }

解析:

  每个Favorites实例都得到一个称作favorites的私有Map<Class<?>, Object>的支持。要注意的是通配符类型是嵌套的:它不是属于通配符类型的Map的类型,而是它的键的类型。由此可见,每个键都可以有一个不同的参数化类型:一个可以是Class<String>,接下来是Class<Integer>等等。异构就是从这里来的。

  它先从favorites映射中获得与指定Class对象相对应的值。这正是要返回的对象引用,但它的编译时类型是错误的。它的类型只是Object(favorites映射的值类型),我们需要返回一个T。因此,getFavorite方法的实现利用Class的cast方法,将对象引用动态地转换(dynamically cast)成了Class对象所表示的类型。

  cast方法是Java的cast操作符的动态模拟。它只检验它的参数是否为Class对象所表示的类型的实例。如果是,就返回参数;否则就抛出ClassCastException异常。

该类存在局限性:

  (1)如果存在恶意的客户端,可以破坏favorites实例的类型安全,只要以它的原生态形式使用class对象。但是会造成客户端代码在编译时产生未受检的警告。

  该种问题解决方案:为了确保运行时类型安全,需要付出代价,让putfavorites方法检验instance是否真的是type所表示的类型实例。

public <T> void putFavorite(Class<T> type, T instance) {
    if (type == null)
        throw new NullPointerException("Type is null");
//        favorites.put(type, instance);
     favorites.put(type, type.cast(instance));
}

(2)不能用在不可具体化类型中。

  如果你想保存String或者String[],但不能保存最喜爱的List<String>。如果试图保存最喜爱的List<String>,程序就不能进行编译。原因在于你无法为List<String>获得一个Class对象:List<String>.Class是个语法错误,这也是件好事。List<String>和List<Integer>共用一个Class对象,即List.class。

  类class提供了一个安全(且动态)执行转换实例的方法。asSubclass类,它将调用它class对象转换成用其参数表示类的一个子类。

实例:

import java.lang.annotation.*;
import java.lang.reflect.*;

public class PrintAnnotation {
    // Use of asSubclass to safely cast to a bounded type token
    static Annotation getAnnotation(AnnotatedElement 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));
    }
    // Test program to print named annotation of named class
    public static void main(String[] args) throws Exception {
        if (args.length != 2) {
            System.out.println("Usage: java PrintAnnotation <class> <annotation>");
            System.exit(1);
        }
        String className = args[0];
        String annotationTypeName = args[1]; 
        Class<?> klass = Class.forName(className);
        System.out.println(getAnnotation(klass, annotationTypeName));
    }
}

总结:(1)集合API说明了泛型一般用法,但是限制每个容器只能有固定数目的类型参数。为了避开这一限制,可以将类型参数放在键上,而不是在容器上。

  (2)对于类型安全的异构容器,可以用class对象作为键。以该方式使用的class对象叫做类型令牌。

posted @ 2018-03-18 22:27  小码农成长记  阅读(385)  评论(0)    收藏  举报