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对象叫做类型令牌。

浙公网安备 33010602011771号