使用 java 枚举实现位域
我维护着一个大型文档档案,并且经常使用位字段来记录处理或验证文档时的状态。我的遗留代码仅使用静态 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
您可以简单地使用内部
ordinal()
值来计算它,而不是定义构造函数参数。请注意,现在您应该避免重新排序、插入(末尾除外)或删除条目,否则标志值将发生变化,并且数据库内容的含义将发生变化。
Instead of defining constructor parameters, you could simply use the internal
ordinal()
value to calculate this.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.
你的方法正是做到这一点的方法。
your approach is exactly the way to do it.
稍微好一点的方法是存储1 <<< 的结果。 this.ordinal()
在字段中时枚举值被构造。这样,您不必手动提供每个值,并且该标志仅计算一次。
A slightly better way would be to store the result of1 << this.ordinal()
in a field whenthe enum values are constructed. This way, you don't have to provide each value manually, and the flag is only computed once.
不要给你的枚举值。使用
EnumSet
将它们组合起来,并在持久化时使用Enum.ordinal()
以便在单个整数之间进行转换。您可能还会发现Class.getEnumConstants()
在从整数重建集合时很有用。Don't give your enums values. Use an
EnumSet
to combine them, and useEnum.ordinal()
when persisting in order to convert to/from a single integer. You might also findClass.getEnumConstants()
useful when reconstructing the set from the integer.我为这个问题制作了一个完整的库:
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.