Java:如何从<! - 实现动态转换?将... - >扩展到<e> </e>

时间:2014-12-02 18:34:37

标签: java generics casting

我有一个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参数而不是枚举。这将至少提供一些编译时保证,即类令牌适合于生成的结果列表的类型。但是,这仍然无法使用枚举中的数据;我还是要在方法调用之前抛出它。

我可以使用支票吗?

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

更新12/6/14

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

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

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

使用参数化的类型安全单例模式,如建议的那样,将使编译器能够检查类型安全性,但我拥有的API已经渗透到这些枚举类中,我觉得我不能将它们重构为常规类。< / p>

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

  • 与枚举常量相关的数据是静态的和最终的;这里也是一成不变的。

  • 我完全控制进入枚举常量的数据,所以我在运行时知道e_tbl字段永远是Class某个类型的Row对象,它扩展了{{ 1}}。

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

@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>)进行编译,因此我必须使用未经检查的强制转换。仍然会生成警告。

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

    由于枚举不适用于泛型类型,我认为没有更好的解决方案。

1 个答案:

答案 0 :(得分:2)

你可以做的一件事:

  • 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类相同,它具有共同的祖先,但这并不意味着您可以将它们相互转换。