JDK 5.0中的更改之一是类java.lang.Class是泛型的。这是一个对容器类以外的东西使用泛型的有趣示例。
既然Class有了类型参数T,您可能会问,T代表什么?它代表Class对象表示的类型。
例如,String.class是Class<String>的类型,Serializable.class是Class<Serializable>的类型。这可以用来提高反射代码的类型安全性。
特别是,由于Class中的newInstance()方法现在返回T,因此在反射创建对象时,您可以获得更精确的类型。
例如,假设您需要编写一个实用程序方法来执行数据库查询(以SQL字符串形式给出),并在数据库中返回与该查询匹配的对象的集合。
一种方法是显式传递工厂对象,编写如下代码:
interface Factory<T> { T make();}public <T> Collection<T> select(Factory<T> factory, String statement) {Collection<T> result = new ArrayList<T>();/* Run sql query using jdbc */for (/* Iterate over jdbc results. */) {T item = factory.make();/* Use reflection and set all of item's* fields from sql results.*/result.add(item);}return result;}
您可以这样调用它:
select(new Factory<EmpInfo>(){public EmpInfo make() {return new EmpInfo();}}, "selection string");
或者您可以声明一个类EmpInfoFactory来实现Factory接口
class EmpInfoFactory implements Factory<EmpInfo> {...public EmpInfo make() {return new EmpInfo();}}
调用它
select(getMyEmpInfoFactory(), "selection string");
该解决方案的缺点是它需要:
- 在调用站点使用冗长的匿名工厂类,或者
- 为每种使用的类型声明一个工厂类,并在调用站点传递一个工厂实例,这有点不自然。
将类字面量用作工厂对象是很自然的,然后可以通过反射使用它。今天(没有泛型),代码可能被编写为:
Collection emps = sqlUtility.select(EmpInfo.class, "select * from emps");...public static Collection select(Class c, String sqlStatement) {Collection result = new ArrayList();/* Run sql query using jdbc. */for (/* Iterate over jdbc results. */ ) {Object item = c.newInstance();/* Use reflection and set all of item's* fields from sql results.*/result.add(item);}return result;}
但是,这将无法为我们提供所需的精确类型的集合。现在Class是泛型的,我们可以改写以下内容:
Collection<EmpInfo>emps = sqlUtility.select(EmpInfo.class, "select * from emps");...public static <T> Collection<T> select(Class<T> c, String sqlStatement) {Collection<T> result = new ArrayList<T>();/* Run sql query using jdbc. */for (/* Iterate over jdbc results. */ ) {T item = c.newInstance();/* Use reflection and set all of item's* fields from sql results.*/result.add(item);}return result;}
上面的代码以类型安全的方式为我们提供了精确的集合类型。
使用类字面量作为运行时类型标记的这种技术是一个非常有用的技巧。例如,这是一种新用法,在新的API中已广泛使用它来处理注释。
