繁体   English   中英

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

[英]Java: How to create an immutable value object with a field that follows the (generic) extensible enum type pattern

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

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

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

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

这是我正在使用的枚举中的一些代码。 这个枚举是一组枚举之一,我用它来描述我在我的应用程序中使用的几个数据库表中的任何一个数据库列的元数据。 每个表都有它自己的enum来描述这些数据,它们都是通过实现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>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...
    :
    :
}    

这是有效的,但该值不被识别为枚举类型,我想使用与EnumSetEnumMap相关联的枚举常量。

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

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

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

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

我在这里使用严重的最小代码,修剪了大量的细节,但我希望你能看到它是如何适合的。

请注意,我用的EnumSet你的,而不是Class ,因为有在更详细EnumSet

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

有趣的是,有多少有用的功能不属于这种模式。 例如,您可以使用EnumSet定义列集,以便立即将unionintersection技巧作为免费赠品。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM