<< 细则 · 目录 · 通配符的更多用法 >>
类字面量作为运行时类型标记 (Class Literals as Runtime-Type Tokens)
JDK5.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();
/* 对查询的结果
* 使用反射设置item的属性
*/
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();
/* 使用jdbc执行sql进行查询 */
for (/* 遍历jdbc查询的结果 */) {
Object item = c.newInstance();
/* 对查询的结果
* 使用反射设置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();
/* 对查询的结果
* 使用反射设置item的属性
*/
result.add(item);
}
return result;
}
上面的代码以一种类型安全的方式提供了精确的集合类型。
这种使用类字面量作为运行时类型标记的技术是一个非常有用的技巧。例如,它在新的api中被广泛用于操作注释。
<< 细则 · 目录 · 通配符的更多用法 >>
本文译自:https://docs.oracle.com/javase/tutorial/extra/generics/literals.html