48

我维护着一个大型文档存档,并且我经常使用位域来记录我的文档在处理过程中或验证它们时的状态。我的遗留代码仅使用静态 int 常量,例如:

static int DOCUMENT_STATUS_NO_STATE = 0
static int DOCUMENT_STATUS_OK = 1
static int DOCUMENT_STATUS_NO_TIF_FILE = 2
static int DOCUMENT_STATUS_NO_PDF_FILE = 4

通过设置适当的标志,可以很容易地指示文档所处的状态。例如:

status = DOCUMENT_STATUS_NO_TIF_FILE | DOCUMENT_STATUS_NO_PDF_FILE;

由于使用静态常量的方法是不好的做法,并且因为我想改进代码,所以我希望使用 Enums 来实现相同的目标。有一些要求,其中之一是将状态作为数字类型保存到数据库中的需要。因此需要将枚举常量转换为数值。以下是我的第一种方法,我想知道这是否是正确的方法?

class DocumentStatus{

    public enum StatusFlag {

        DOCUMENT_STATUS_NOT_DEFINED(1<<0),
        DOCUMENT_STATUS_OK(1<<1), 
        DOCUMENT_STATUS_MISSING_TID_DIR(1<<2),
        DOCUMENT_STATUS_MISSING_TIF_FILE(1<<3),
        DOCUMENT_STATUS_MISSING_PDF_FILE(1<<4),
        DOCUMENT_STATUS_MISSING_OCR_FILE(1<<5),
        DOCUMENT_STATUS_PAGE_COUNT_TIF(1<<6),
        DOCUMENT_STATUS_PAGE_COUNT_PDF(1<<7),
        DOCUMENT_STATUS_UNAVAILABLE(1<<8);


        private final long statusFlagValue;

        StatusFlag(long statusFlagValue) {
            this.statusFlagValue = statusFlagValue;
        }

        public long getStatusFlagValue(){
            return statusFlagValue;
        } 

       }


    /**
     * Translates a numeric status code into a Set of StatusFlag enums
     * @param numeric statusValue 
     * @return EnumSet representing a documents status
     */
    public EnumSet<StatusFlag> getStatusFlags(long statusValue) {
        EnumSet statusFlags = EnumSet.noneOf(StatusFlag.class);
        StatusFlag.each { statusFlag -> 
            long flagValue = statusFlag.statusFlagValue
            if ( (flagValue&statusValue ) == flagValue ) {
               statusFlags.add(statusFlag);
            }
        }
        return statusFlags;
    }


    /**
     * Translates a set of StatusFlag enums into a numeric status code
     * @param Set if statusFlags
     * @return numeric representation of the document status 
     */
    public long getStatusValue(Set<StatusFlag> flags) {
        long value=0;
        flags.each { statusFlag -> 
            value|=statusFlag.getStatusFlagValue() 
        }
        return value;
    }

     public static void main(String[] args) {

        DocumentStatus ds = new DocumentStatus();
        Set statusFlags = EnumSet.of(
            StatusFlag.DOCUMENT_STATUS_OK,
            StatusFlag.DOCUMENT_STATUS_UNAVAILABLE);

        assert ds.getStatusValue( statusFlags )==258 // 0000.0001|0000.0010

        long numericStatusCode = 56;
        statusFlags = ds.getStatusFlags(numericStatusCode);

        assert !statusFlags.contains(StatusFlag.DOCUMENT_STATUS_OK);
        assert statusFlags.contains(StatusFlag.DOCUMENT_STATUS_MISSING_TIF_FILE);
        assert statusFlags.contains(StatusFlag.DOCUMENT_STATUS_MISSING_PDF_FILE);
        assert statusFlags.contains(StatusFlag.DOCUMENT_STATUS_MISSING_OCR_FILE);

    }

}
4

5 回答 5

34

ordinal()您可以简单地使用内部值来计算它,而不是定义构造函数参数。

public enum StatusFlag {

    DOCUMENT_STATUS_NOT_DEFINED,
    DOCUMENT_STATUS_OK, 
    DOCUMENT_STATUS_MISSING_TID_DIR,
    DOCUMENT_STATUS_MISSING_TIF_FILE,
    DOCUMENT_STATUS_MISSING_PDF_FILE,
    DOCUMENT_STATUS_MISSING_OCR_FILE,
    DOCUMENT_STATUS_PAGE_COUNT_TIF,
    DOCUMENT_STATUS_PAGE_COUNT_PDF,
    DOCUMENT_STATUS_UNAVAILABLE;


    public long getStatusFlagValue(){
        return 1 << this.ordinal();
    } 

}

请注意,现在您应该避免重新排序、插入(除了末尾之外)或删除条目,否则标志值会改变,数据库内容的含义也会改变。

于 2011-03-18T01:27:37.093 回答
20

您的方法正是这样做的方法。

于 2011-03-18T00:14:59.567 回答
9

更好的方法是1 << this.ordinal()在构造枚举值时将结果存储在字段中。这样,您不必手动提供每个值,并且标志只计算一次。

公共枚举状态标志 {
  DOCUMENT_STATUS_NOT_DEFIND,
  DOCUMENT_STATUS_OK, 
  DOCUMENT_STATUS_MISSING_TID_DIR,
  DOCUMENT_STATUS_MISSING_TIF_FILE,
  DOCUMENT_STATUS_MISSING_PDF_FILE,
  DOCUMENT_STATUS_MISSING_OCR_FILE,
  DOCUMENT_STATUS_PAGE_COUNT_TIF,
  DOCUMENT_STATUS_PAGE_COUNT_PDF,
  DOCUMENT_STATUS_UNAVAILABLE;

  public final int flag;

  StatusFlag() { 
    this.flag = 1 << this.ordinal();
  } 
}
**更新:** 这是我没有太多 Java 经验时的旧答案。我不再认为我的答案是有效的,因为这种方法将标志的值与排序或枚举值相结合,这很糟糕:如果更改顺序或删除枚举值,这将影响其他枚举值的标志,这可能会产生无法预料的后果。

这些天来,我会使用问题中使用的方法(通过构造函数参数手动提供标志的值),因为它更易于维护:

public enum StatusFlag {

  DOCUMENT_STATUS_NOT_DEFINED(0),
  DOCUMENT_STATUS_OK(1), 
  DOCUMENT_STATUS_MISSING_TID_DIR(2),
  DOCUMENT_STATUS_MISSING_TIF_FILE(3),
  DOCUMENT_STATUS_MISSING_PDF_FILE(4),
  DOCUMENT_STATUS_MISSING_OCR_FILE(5),
  DOCUMENT_STATUS_PAGE_COUNT_TIF(6),
  DOCUMENT_STATUS_PAGE_COUNT_PDF(7),
  DOCUMENT_STATUS_UNAVAILABLE(8);

  public final int flag;

  StatusFlag(int id) { 
    this.flag = 1 << id;
  } 
}
于 2012-12-20T21:14:24.063 回答
6

不要给你的枚举值。使用 anEnumSet来组合它们,并Enum.ordinal()在持久化时使用以转换为单个整数/从单个整数转换。Class.getEnumConstants()从整数重构集合时,您可能还会发现它很有用。

于 2011-03-18T00:07:34.317 回答
4

我为这个问题制作了一个完整的库:http: //claude-martin.ch/enumbitset/

主要目标是将枚举类型集存储在位域中。但它也支持其他类型。

有了这个,你就不需要任何额外的方法,比如你的“getStatusFlags()”。只需添加接口EnumBitSetHelper即可在任何现有枚举类型上使用它(它像“特征”一样使用)。然后每个枚举常量可以创建一个“EnumBitSet”,它具有 Java 的 EnumSet 和 BitSet 的所有方法。然后,您可以使用这些枚举常量集并将它们转换为位域值。

它支持 BigInteger 和 long 等多种格式,可以轻松地将值存储到位字段中。但请注意,这只适用于 Java 8 及更高版本。

于 2014-03-13T10:57:04.517 回答