使用 java 枚举实现位域

发布于 2024-10-23 19:51:39 字数 3010 浏览 1 评论 0原文

我维护着一个大型文档档案,并且经常使用位字段来记录处理或验证文档时的状态。我的遗留代码仅使用静态 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;

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

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

    }

}

I maintain a large document archive and I often use bit fields to record the status of my documents during processing or when validating them. My legacy code simply uses static int constants such as:

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

This makes it pretty easy to indicate the state a document is in, by setting the appropriate flags. For example:

status = DOCUMENT_STATUS_NO_TIF_FILE | DOCUMENT_STATUS_NO_PDF_FILE;

Since the approach of using static constants is bad practice and because I would like to improve the code, I was looking to use Enums to achieve the same. There are a few requirements, one of them being the need to save the status into a database as a numeric type. So there is a need to transform the enumeration constants to a numeric value. Below is my first approach and I wonder if this is the correct way to go about this?

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

    }

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

暮年 2024-10-30 19:51:39

您可以简单地使用内部 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();
    } 

}

请注意,现在您应该避免重新排序、插入(末尾除外)或删除条目,否则标志值将发生变化,并且数据库内容的含义将发生变化。

Instead of defining constructor parameters, you could simply use the internal ordinal() value to calculate this.

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

}

Please note that now you should abstain from reordering, inserting (other than at the end) or deleting entries, otherwise the flag values will change, and the meaning of your database contents will change.

丑疤怪 2024-10-30 19:51:39

你的方法正是做到这一点的方法。

your approach is exactly the way to do it.

爱的那么颓废 2024-10-30 19:51:39

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

 public enum StatusFlag {
  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();
  } 
}


**Update:** This is an old answer from back when I did not have much Java experience.
I no longer think my answer is valid, as this approach couples the value of the flag to the ordering or the enum values, which is bad: if the order is changed or enum values are removed, this will affect the flags of other enum values, which can have unforeseen consequences.

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

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

A slightly better way would be to store the result of 1 << this.ordinal() in a field when
the enum values are constructed. This way, you don't have to provide each value manually, and the flag is only computed once.

 public enum StatusFlag {
  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();
  } 
}


**Update:** This is an old answer from back when I did not have much Java experience.
I no longer think my answer is valid, as this approach couples the value of the flag to the ordering or the enum values, which is bad: if the order is changed or enum values are removed, this will affect the flags of other enum values, which can have unforeseen consequences.

These days, I would use the approach used in the question (manually provide the value of the flag via a constructor parameter) as it is more maintainable:

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;
  } 
}
北渚 2024-10-30 19:51:39

不要给你的枚举值。使用 EnumSet 将它们组合起来,并在持久化时使用 Enum.ordinal() 以便在单个整数之间进行转换。您可能还会发现 Class.getEnumConstants() 在从整数重建集合时很有用。

Don't give your enums values. Use an EnumSet to combine them, and use Enum.ordinal() when persisting in order to convert to/from a single integer. You might also find Class.getEnumConstants() useful when reconstructing the set from the integer.

冧九 2024-10-30 19:51:39

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

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

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

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

I have made a complete library for this problem:
http://claude-martin.ch/enumbitset/

The main goal was to store sets of enum types in bitfields. But it also supports other types.

With this you would not need any extra methods like your "getStatusFlags()". It can be used on any existing enum type simply by adding the interface EnumBitSetHelper (it is used like a "trait").
Each enum constant can then create an "EnumBitSet" which has all methods of Java's EnumSet and BitSet.
Then you can work with these sets of enum constants and convert them to bitfield values.

It supports many formats such as BigInteger and long to easily store the value into a bit field.
But note that this only works with Java version 8 and newer.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文