有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

泛型Java:如何从<>实现动态转换?扩展…>到<E>

我有一个enum存储数据库表的一些元数据:

static enum DataTable implements TableMetaData {

    HISTORIES ("h", Hx.class, HxColumn.HPI_ID),
    :
    :

    private final String                              e_alias;
    private final Class<? extends Row>                e_tbl;
    private final ColumnMetaData                      e_idCol;

    DataTable(String                                  al,
          Class           <? extends Row>             c1, 
          ColumnMetaData                              id)
    {...}

    @Override public Class<? extends Row> modelClass() { return this.e_tbl; }
    :
    :
}

感兴趣的字段是e_tbl,它存储与数据库表对应的模型对象类。此项目中的所有模型对象类都实现了Row接口。我希望能够访问此字段以生成此类的实例

我的问题是,我使用的是一个未经检查的强制转换,我不知道它是否安全,而且我还没有弄清楚如何执行运行时检查以确保模型对象匹配。以下是发生强制转换的代码:

static final <E extends Row> List<E> doQuery (
            final List<JdbcValue> pars,
            final String sql, 
            final TableMetaData dm
    ) 
{

    List<E> result = new ArrayList<E>();

    @SuppressWarnings("unchecked")
    Class<E> cls = (Class<E>) dm.modelClass();
    :
    :
}

我认为这是一个危险的演员阵容。该查询旨在生成E实例,这些实例将使用提供的枚举常量中包含的模型类生成。问题是我无法知道E与枚举中存储的类标记匹配。在这种情况下Class.asSubclass()似乎无法帮助我

问题是我有一个字段是<? extends Row>,我需要使它与<E extends Row>兼容

一种解决方案是将方法重构为需要Class<E> cls参数而不是枚举。这将至少提供一些编译时保证,即类标记适合生成的结果列表的类型。但是,这仍然不能使我使用枚举中的数据;我仍然必须在方法调用之前强制转换它

有支票我可以用吗

==================================================

于2014年6月12日更新

在我看来,这是一个没有好的、干净的解决方案的问题

枚举不支持泛型类型参数(它们不能)

每次尝试在枚举常量中存储参数化类型时,编译时都会出现不兼容的类型信息

正如建议的那样,使用参数化的类型安全单例模式将使编译器能够检查类型安全性,但我拥有的API被这些枚举类所渗透,我觉得我无法将它们重构为常规类

为了限制损失,我想知道以下推理是否准确:

  • 与枚举常量关联的数据是静态的和最终的;在这里,它也是不变的

  • 我完全控制进入枚举常量的数据,因此在运行时我知道e_tbl字段将始终是某种类型的Class对象,它扩展了Row

这些假设承认了我认为具有相同缺陷的两种方法。在enum类中,访问器方法重写为:

@Override public <E extends Row> Class<E> modelclass() { 
    @SuppressWarning("unchecked")
    Class<E> cls = (Class<E>) this.e_tbl;
    return this.e_tbl; }
  1. 我可以将枚举中的数据存储到使用原始类型的字段中。原始类型与泛型访问器方法互操作,因此它会编译,但会生成警告

  2. 我可以将枚举中的数据存储到使用<? extends Row>通配符类型的字段中。这不会使用泛型访问器方法(<E> extends Row>)编译,因此我必须使用未经检查的强制转换。仍会生成警告

因为我知道枚举数据总是扩展Row,所以我认为这可能是可以的。我不能保证所检索的枚举数据适合于所生成的List类型。但我认为除了记录API用户必须为返回的查询发送正确的TableMetaData常量,否则结果将是“未定义”的之外,没有任何方法可以对此进行控制

由于枚举不能处理泛型类型,我认为没有更好的解决方案


共 (1) 个答案

  1. # 1 楼答案

    你可以做一件事:

    • TableMetaData变成TableMetaData<E>,并根据E定义类
    • 去掉enum(因为它们不支持类型参数),编写一个类 每个枚举条目

    所以界面看起来大致像

    class Histories implements TableMetaData<SpecialRowType> {
         Class<SpecialRowType> modelClass();
    }
    

    这样你就可以使用它了

    static final <E extends Row> List<E> doQuery (
                final List<JdbcValue> pars,
                final String sql, 
                final TableMetaData<E> dm
        ) 
    {
    
        List<E> result = new ArrayList<E>();
        // type safe, because E refers to the same thing.
        Class<E> cls = dm.modelClass();
        :
    }
    

    现在,编译器可以确保? extends Row类与E extends Row类相同,它们确实有一个共同的祖先,但这并不意味着可以将它们相互转换