类Class已经泛型化了,但是很多人一开始都感觉其泛型化的方式很混乱。Class<T>中类型参数T的含义是什么?事实证明它是所引用的类接口。怎么会是这样的呢?那是一个循环推理?如果不是的话,为什么这样定义它?
在以前的 JDK 中,Class.newInstance()方法的定义返回Object,您很可能要将该返回类型强制转换为另一种类型:
class Class { Object newInstance(); } |
但是使用泛型,您定义Class.newInstance()方法具有一个更加特定的返回类型:
class Class<T> { T newInstance(); } |
如何创建一个Class<T>类型的实例?就像使用非泛型代码一样,有两种方式:调用方法Class.forName()或者使用类常量X.class。Class.forName()被定义为返回Class<?>。另一方面,类常量X.class被定义为具有类型Class<X>,所以String.class是Class<String>类型的。
让Foo.class是Class<Foo>类型的有什么好处?大的好处是,通过类型推理的魔力,可以提高使用反射的代码的类型安全。另外,还不需要将Foo.class.newInstance()强制类型转换为Foo。
考虑一个方法,它从数据库检索一组对象,并返回 JavaBeans 对象的一个集合。您通过反射来实例化和初始化创建的对象,但是这并不意味着类型安全必须完全被抛至脑后。考虑下面这个方法:
public static<T> List<T> getRecords(Class<T> c, Selector s) { // Use Selector to select rows List<T> list = new ArrayList<T>(); for (/* iterate over results */) { T row = c.newInstance(); // use reflection to set fields from result list.add(row); } return list; } |
可以像下面这样简单地调用该方法:
List<FooRecord> l = getRecords(FooRecord.class, fooSelector); |
编译器将会根据FooRecord.class是Class<FooRecord>类型的这一事实,推断getRecords()的返回类型。您使用类常量来构造新的实例并提供编译器在类型检查中要用到的类型信息。