类文字作为运行时类型标记
原文: https://docs.oracle.com/javase/tutorial/extra/generics/literals.html
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 中。