本教程是针对JDK 8编写的。本页面中描述的示例和实践不利用后续版本中引入的改进,并且可能使用不再可用的技术。
请参阅Java语言更改以获取Java SE 9及后续版本中更新的语言特性摘要。
请参阅JDK发布说明以获取所有JDK版本的新功能、增强功能和已删除或已弃用选项的信息。
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>(); /* 使用jdbc运行sql查询 */ for (/* 迭代jdbc结果 */) { T item = factory.make(); /* 使用反射从sql结果中设置item的所有字段 */ result.add(item); } return result; }
你可以这样调用:
select(new Factory<EmpInfo>(){ public EmpInfo make() { return new EmpInfo(); }}, "查询字符串");
或者你可以声明一个支持Factory
接口的EmpInfoFactory
类
class EmpInfoFactory implements Factory<EmpInfo> { ... public EmpInfo make() { return new EmpInfo(); } }
然后这样调用:
select(getMyEmpInfoFactory(), "查询字符串");
这种解决方案的缺点是要么在调用处使用冗长的匿名工厂类,要么为每种使用的类型声明一个工厂类并在调用处传递一个工厂实例,这有些不自然。
将类字面量作为工厂对象并通过反射使用它是很自然的。在今天(没有泛型)的代码可能是这样编写的:
Collection emps = sqlUtility.select(EmpInfo.class, "select * from emps"); ... public static Collection select(Class c, String sqlStatement) { Collection result = new ArrayList(); /* 使用jdbc运行sql查询 */ for (/* 迭代jdbc结果 */) { Object item = c.newInstance(); /* 使用反射从sql结果中设置item的所有字段 */ 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>(); /* 使用jdbc运行sql查询。 */ for (/* 遍历jdbc的结果。 */ ) { T item = c.newInstance(); /* 使用反射从sql结果中设置所有item的字段。 */ result.add(item); } return result; }
上面的代码以类型安全的方式给我们提供了精确类型的集合。
使用类字面量作为运行时类型标记的技巧非常有用。例如,在新的用于操作注解的API中广泛使用这种习惯用法。