Java:如何使用遵循(通用)可扩展枚举类型模式的字段创建不可变值对象

Java:如何使用遵循(通用)可扩展枚举类型模式的字段创建不可变值对象,java,generics,interface,enums,Java,Generics,Interface,Enums,在《有效Java》第二版中,Joshua Bloch开发了“可扩展枚举模式”(第34项)。由于枚举不能子类化,他建议通过让每个枚举实现一个公共类型,即接口,来统一相关枚举的组。这允许通过统一类型名引用枚举。此解决方案的一个明显问题是,类型安全性在某种程度上受到了影响,因为至少在理论上可以通过创建一个实现统一接口的类来替换任何非枚举对象 为了解决这个问题,提出了一种解决方案。下面是书中使用的方法声明,它采用了枚举。示例应用程序有两个枚举(BasicOperation和ExtendedOperati

在《有效Java》第二版中,Joshua Bloch开发了“可扩展枚举模式”(第34项)。由于枚举不能子类化,他建议通过让每个枚举实现一个公共类型,即接口,来统一相关枚举的组。这允许通过统一类型名引用枚举。此解决方案的一个明显问题是,类型安全性在某种程度上受到了影响,因为至少在理论上可以通过创建一个实现统一接口的类来替换任何非枚举对象

为了解决这个问题,提出了一种解决方案。下面是书中使用的方法声明,它采用了枚举。示例应用程序有两个枚举(
BasicOperation
ExtendedOperation
),这两个枚举都实现了一个名为
Operation
的统一类型接口。该方法旨在接受任何适当类型的枚举:

private static <T extends Enum<T> & Operation> void test(
        Class<T> opset, double x, double y) {
    :
    :
}
我想在我的应用程序中使用一个名为
Datum
的值类。它旨在将数据库列的值与其列枚举保持在一起

我的问题是:

我不能在
数据的构造函数中使用泛型方法参数。如何告诉编译器,
Datum
中的一个字段必须同时实现
ColumnMetaData
Enum
?目前,我正在使用以下工具:

static class Datum {

    private final Object                        val;  
    private final ColumnMetaData<? extends DB > col;

    private Datum(Object val, ColumnMetaData<? extends DB> col) {
        this.val = val;
        this.col = col;
    }

    // assorted static factories here...
    :
    :
}    
静态类基准{
私人最终目的;

private final ColumnMetaData这里有一种方法——我使用过它,它工作得很好

使用具有泛型参数的基类:

public class Table<Column extends Enum<Column> & Table.Columns> {
  // Name of the table.
  protected final String tableName;
  // All of the columns in the table. This is actually an EnumSet so very efficient.
  protected final Set<Column> columns;

  /**
   * The base interface for all Column enums.
   */
  public interface Columns {
    // What type does it have in the database?
    public Type getType();
  }

  // Small list of database types.
  public enum Type {
    String, Number, Date;
  }

  public Table(String tableName,
               Set<Column> columns) {
    this.tableName = tableName;
    this.columns = columns;
  }
}
公共类表{
//表的名称。
受保护的最终字符串表名;
//表中的所有列。这实际上是一个非常有效的枚举集。
受保护的最终设置列;
/**
*所有列枚举的基接口。
*/
公共接口列{
//它在数据库中有什么类型?
公共类型getType();
}
//数据库类型的小列表。
公共枚举类型{
字符串、数字、日期;
}
公共表(字符串tableName,
设置列){
this.tableName=tableName;
this.columns=列;
}
}
然后将其子类化,并为所需的每个表提供实现接口的具体枚举

public class VersionTable extends Table<VersionTable.Column> {
  public enum Column implements Table.Columns {
    Version(Table.Type.String),
    ReleaseDate(Table.Type.Date);
    final Table.Type type;

    Column(Table.Type type) {
      this.type = type;
    }

    @Override
    public Type getType() {
      return type;
    }
  }

  public VersionTable() {
    super("Versions", EnumSet.allOf(Column.class));
  }
}
公共类VersionTable扩展表{
公共枚举列实现表.Columns{
版本(Table.Type.String),
发布日期(表.类型.日期);
最终表格。类型;
列(Table.Type类型){
this.type=type;
}
@凌驾
公共类型getType(){
返回类型;
}
}
公共版本表(){
super(“Versions”,EnumSet.allOf(Column.class));
}
}
我在这里使用了非常少的代码,删除了大量的细节,但我希望您能看到它是如何适合的

注意,我使用了
EnumSet
而不是
,因为
EnumSet
中有更多细节

还要注意的是,最大的类型安全性得到了保护


有趣的是,有多少有用的功能属于这种模式。例如,您可以使用
EnumSet
定义列集,这样您就可以立即获得
union
intersection
免费技巧。

尝试generify
Datum
类此问题可能与此问题重复,因此,为了清楚起见,您可以声明st类似于
的atic工厂方法还有:“我如何告诉编译器数据中的一个字段必须同时实现
ColumnMetaData
Enum
”-我觉得这一定是输入错误。如果字段必须是
Enum
,那么它必须是一个
表。列
,并且根本不需要泛型。@PaulBellora:我认为我的问题是书中所述问题的扩展。我想以某种方式将参数传递给构造函数,以便编译器理解e参数是实现ColumnMetaData的枚举。我不想泛化Datum,因为Datum的上下文中没有相关列,即Datum不会严格依赖于类型参数。我没有显示所有代码,但Datum有两个重载构造函数……一个接受ColumnMetaData,另一个接受ColumnMetaData没有。
public class VersionTable extends Table<VersionTable.Column> {
  public enum Column implements Table.Columns {
    Version(Table.Type.String),
    ReleaseDate(Table.Type.Date);
    final Table.Type type;

    Column(Table.Type type) {
      this.type = type;
    }

    @Override
    public Type getType() {
      return type;
    }
  }

  public VersionTable() {
    super("Versions", EnumSet.allOf(Column.class));
  }
}