类文字作为运行时类型标记

原文: 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 字符串形式给出,并返回数据库中与该查询匹配的对象集合。

一种方法是显式传入工厂对象,编写如下代码:

  1. interface Factory<T> { T make();}
  2. public <T> Collection<T> select(Factory<T> factory, String statement) {
  3. Collection<T> result = new ArrayList<T>();
  4. /* Run sql query using jdbc */
  5. for (/* Iterate over jdbc results. */) {
  6. T item = factory.make();
  7. /* Use reflection and set all of item's
  8. * fields from sql results.
  9. */
  10. result.add(item);
  11. }
  12. return result;
  13. }

您可以将其称为

  1. select(new Factory<EmpInfo>(){
  2. public EmpInfo make() {
  3. return new EmpInfo();
  4. }}, "selection string");

或者您可以声明一个类EmpInfoFactory来支持Factory接口

  1. class EmpInfoFactory implements Factory<EmpInfo> {
  2. ...
  3. public EmpInfo make() {
  4. return new EmpInfo();
  5. }
  6. }

并称之为

  1. select(getMyEmpInfoFactory(), "selection string");

该解决方案的缺点是它需要:

  • 在调用站点使用详细的匿名工厂类,或
  • 为每个使用的类型声明一个工厂类,并在调用站点传递工厂实例,这有点不自然。

将类文字用作工厂对象是很自然的,然后可以通过反射使用它。今天(没有泛型)代码可能写成:

  1. Collection emps = sqlUtility.select(EmpInfo.class, "select * from emps");
  2. ...
  3. public static Collection select(Class c, String sqlStatement) {
  4. Collection result = new ArrayList();
  5. /* Run sql query using jdbc. */
  6. for (/* Iterate over jdbc results. */ ) {
  7. Object item = c.newInstance();
  8. /* Use reflection and set all of item's
  9. * fields from sql results.
  10. */
  11. result.add(item);
  12. }
  13. return result;
  14. }

但是,这不会给我们提供我们想要的精确类型的集合。既然Class是通用的,我们可以编写以下内容:

  1. Collection<EmpInfo>
  2. emps = sqlUtility.select(EmpInfo.class, "select * from emps");
  3. ...
  4. public static <T> Collection<T> select(Class<T> c, String sqlStatement) {
  5. Collection<T> result = new ArrayList<T>();
  6. /* Run sql query using jdbc. */
  7. for (/* Iterate over jdbc results. */ ) {
  8. T item = c.newInstance();
  9. /* Use reflection and set all of item's
  10. * fields from sql results.
  11. */
  12. result.add(item);
  13. }
  14. return result;
  15. }

上面的代码以类型安全的方式为我们提供了精确的集合类型。

这种使用类文字作为运行时类型标记的技术是一个非常有用的技巧。例如,这是一种习惯用法,广泛用于操作注释的新 API 中。