无法使用枚举值成员的类型声明泛型类

时间:2014-04-25 20:22:21

标签: java generics enums

我尝试以一种使用类型的方式声明泛型类,这种类型可能会发生变化,而不必在每次类型更改时重构我的泛型类。例如,如果枚举值a的成员AString,则声明Example<a>与声明Example<String>一样好。

但是,我无法宣布它,因为我似乎无法解决我尝试对某种类型做的事情。我将尝试提供一份我的详细信息的简要示例,然后我将能够更详细地描述问题。

代码示例

enum声明表中的列类型。 (它基本上是从SQL数据类型到Java数据类型的映射。)

public enum ColTypes {
    VARCHAR( String.class ),
    INTEGER( Integer.class );

    public final Class<?> dataType;
    public ColTypes (Class<?> dataType) {
        this.dataType = dataType;
    }
}

此接口表示一个列本身。

public interface Column<T> {
    public Object getColVal(T t);
}

下面是一个表示某些表中行的类的示例。但是,我有许多不同类型的这些类,所以我实际上设置了一些脚本来根据数据库模式生成此代码。它们本质上是Dumb&lt; Data Objects,可能比这更复杂......

为了让这样的类的客户端在表中为该类的对象指定一列,我在其中包含enumColumn。这样,您就可以使用课程的Column&#39;内部枚举作为参数,因此您可以getColVal()通常而不是需要指定特定的getter。看:

public class DBRow {
    private String foo;
    private Integer bar;
    public DBRow(String foo, Integer bar) {
        this.foo = foo;
        this.bar = bar;
    }
    public String getFoo() {
        return this.foo;
    }
    public Integer getBar() {
        return this.bar;
    }

    public enum DBCols implements Column<DBRow> {
        FOO( ColTypes.VARCHAR ) {
            public Object getColVal(DBRow r) {
                return r.getFoo();
            }
        },
        BAR( ColTypes.INTEGER ) {
            public Object getColVal(DBRow r) {
                return r.getBar();
            }
        };

        public final ColType colType;
        public DBCols(ColType colType) {
            this.colType = colType;
        }
    }
}

因此,您可以假设某些DBRow row,请致电DBRow.FOO.getValue(row)而不是row.getFoo()。用于传递DBRow.FOODBRow.BAR作为参数!

这个类是我所拥有的类的一个非常简化的版本,它使用这个策略来处理数据库对象的集合。

public abstract class ColValsGetter<T> {
    protected List<T> objs;
    protected Column<T> col;
    protected ColValsGetter(List<T> objs, Column<T> col) {
        this.objs = objs;
        this.col = col;
    }
    protected Object getColValForObj(int index) {
        return col.getColVal(objs.get(index));
    }
}

这是一个如何在具体类中使用它的简单示例。我也会在BAR列中找到一个。我不使这些类具有通用性的原因是,使用每列的逻辑对于列非常特殊,因此很难抽象出大部分行为,以便我可以使这些{{1将它们使用的ColValsGetter类型作为类型参数。

Column

问题

现在,我的问题是我们架构中的类型可能会发生变化。 (我知道,听起来很可疑,但只是忽略它。)如果public class DBRowFooColValsGetter extends ColValsGetter<DBRow> { private String fooVal; public DBRowFooColValsGetter(List<DBRow> rows) { super(rows, DBRow.DBCols.FOO); } public void complexLogicForFooOnly(int i) { fooVal = (String) getColValForObj(i); // do a lot of stuff that only makes sense for the FOO column }; } 列更改为String类型,那么我ClassCastException的投射会产生FOO。我更愿意声明INTEGER并让其ColValsGetter<T, E>方法返回getColValForObj(int)。但是,将它声明为E之类的东西并不好,因为它遇到了同样的问题。我想宣布的是:

ColValsGetter<DBRow, String>

这样,如果架构发生变化,该类将自动调整。 (当我这样说时,请相信我,即使我不能抽象出这些public class DBRowFooColValsGetter extends ColValsGetter<DBRow, DBRow.DBCols.FOO.colType.dataType> 的行为以使它们成为列的通用,我也可以抽象数据类型。)

然而,我尝试这样做的尝试失败了。 Eclipse说ColValsGetter无法解析为某种类型。我我理解为什么,因为DBRow.DBCols.FOOdataType对象,但是当我在最后添加Class时,我得到语法错误(&#34;预期的标识符。&#34;)我不能在编译时从枚举值中得到我想要的类型吗?

我真的希望以某种方式完成这种行为。有任何想法吗?或者这不可能吗?

1 个答案:

答案 0 :(得分:0)

我认为您希望能够更改枚举并让其他所有内容保持可编辑状态。

这很不可能,因为类型并不是Java中真正的一流数据。您的客户端代码需要使用强类型对象,并且您无法在运行时从异构枚举或任何其他异构数据结构中获取该对象。

你可以在枚举中包含像功能对象这样的getter,并使用委托来嵌入业务逻辑,但你仍然会最终投射。

public class ColValsGetter<T> {
    protected List<T> objs;
    protected Column<T> col;
    public ColValsGetter(List<T> objs, Column<T> col) {
        this.objs = objs;
        this.col = col;
    }
    public T getColValForObj(int index) {
        return col.getColVal(objs.get(index));
    }
}
public class ColValsGetterFactory< T > {
    public ColValsGetterFactory() {}
    public ColValsGetter< T > make( List< T > lt, Column< T > col ) {
        return new ColValsGetter< T >( lt, col );
    }
}
public enum ColTypes {
    VARCHAR( String.class, new ColValsGetterFactory< String >() ),
    INTEGER( Integer.class, new ColValsGetterFactory< Integer >() );

    public final Class<?> dataType;
    public final ColValsGetterFactory< ? > gf;
    public < T > ColTypes ( Class< T > dataType, ColValsGetterFactory< T > gf ) {
        this.dataType = dataType;
        this.gf = gf;
    }
}

public class DBRowFooColValsGetter {
    private String fooVal;
    private ColValsGetter< String > getter;
    public DBRowFooColValsGetter( List< DBRow > rows ) {
        this.getter = ( ColValsGetter< String > )DBRow.DBCols.FOO.colType.gf.make( rows, DBRow.DBCols.FOO );
    }
    public void complexLogicForFooOnly( int i ) {
        fooVal = getter.getColValForObj( i );
        // do a lot of stuff that only makes sense for the FOO column
    };
}