3

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

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

private static <T extends Enum<T> & Operation> void test(
        Class<T> opset, double x, double y) {
    :
    :
}

这样做的原因是泛型方法类型参数确保作为函数的第一个参数提供的类文字既是枚举类型又是操作类型。

这是我正在使用的枚举中的一些代码。这个枚举是一组枚举中的一个,我用来描述我在应用程序中使用的几个数据库表中的任何一个数据库列的元数据。每个表都有自己的枚举来描述这些数据,并且它们都通过实现ColumnMetaData<T>接口来统一(T对应于数据库表的类)。

class Client extends DB {  // Class for the Clients table

    // MetaData for all the columns in Client
    static enum Column implements ColumnMetaData<Client> {

            CLIENT_ID   (...
                :
                :
            );
    }
}

我想在我的应用程序中使用一个名为Datum. 它旨在将数据库列的值与其列枚举保持在一起。

这是我的问题:

我不能在构造函数中使用泛型方法参数Datum。我如何告诉编译器其中一个字段Datum必须同时实现ColumnMetaData<table>and Enum<table.Column>?目前,我正在使用以下内容:

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...
    :
    :
}    

这有效,但该值不被识别为枚举类型,我想将关联的枚举常量与EnumSetand一起使用EnumMap

有没有我没有看到的优雅解决方案?

4

1 回答 1

1

这是一种方法 - 我已经使用它并且效果很好。

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

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;
  }
}

然后对其进行子类化并为您想要的每个表提供一个实现接口的具体枚举。

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));
  }
}

我在这里使用了非常少的代码,并删除了大量细节,但我希望你能看到它是如何适合的。

请注意,我使用的是 anEnumSet而不是您的,Class因为EnumSet.

另请注意,保留了最大的类型安全性。

有趣的是,这种模式中有多少有用的特性。例如,您可以使用这样定义列集,EnumSet以便您立即拥有unionintersection技巧作为免费赠品。

于 2013-09-09T20:15:26.853 回答