使用java枚举实现位字段
我维护一个大型文档归档,并且我经常使用位字段记录文档在处理过程中或验证文档时的状态。我的旧代码只使用静态int常量,例如:使用java枚举实现位字段,java,enums,enumset,Java,Enums,Enumset,我维护一个大型文档归档,并且我经常使用位字段记录文档在处理过程中或验证文档时的状态。我的旧代码只使用静态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 这使得通过设置适当的标志来指示文档所处的状态非常容易。
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);
}
}
类文档状态{
公共枚举状态标志{
文档状态未定义(1不提供枚举值。使用EnumSet
组合它们,并在持久化时使用Enum.ordinal()
,以便转换为单个整数或从单个整数转换。您还可以找到Class.getEnumConstants()
在从整数重建集合时非常有用。您的方法正是这样做的。不需要定义构造函数参数,只需使用内部序数()
值即可计算
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();
}
}
公共枚举状态标志{
文件状态未定义,
文档\状态\正常,
文件\u状态\u缺失\u TID\u目录,
文件状态缺少文件,
文档\u状态\u缺少\u PDF\u文件,
文档\u状态\u缺少\u OCR\u文件,
文件状态页面计数,
文档\状态\页面\计数\ PDF,
文件\状态\不可用;
公共长getStatusFlagValue(){
返回1一个稍微好一点的方法是存储1的结果。我已经为这个问题建立了一个完整的库:
其主要目标是在位字段中存储枚举类型集,但它也支持其他类型
这样,您就不需要任何额外的方法,如“getStatusFlags()”。只需添加接口EnumBitSetHelper(它像“trait”一样使用),就可以在任何现有的枚举类型上使用它。
然后,每个enum常量可以创建一个“EnumBitSet”,其中包含Java的EnumSet和BitSet的所有方法。
然后可以使用这些枚举常量集,并将它们转换为位字段值
它支持多种格式,如BigInteger和long,以便轻松地将值存储到位字段中。
但请注意,这仅适用于Java版本8及更高版本。为什么您不能编写:静态int文档\u状态\u否\u PDF\u文件=1修改后的代码;如果不正确,请还原。另请参见。@trashgod链接似乎已失效。是另一个。@SeyedJalalHosseini:另请参见第32项:使用枚举集而不是引用的位字段。在枚举值中存储额外数据是不正确的毫无意义,因为ordinal()
已经这样做了。@OrangeDog,我不同意。ordinal()
如果重构枚举,则可能会发生更改。请考虑这意味着每个元素在枚举中的位置。但是,如果存储额外的数据,则可以自己控制它。有没有一种方法可以在不使用难看的方法的情况下对其执行布尔逻辑:StatusFlag.DOCUMENT\u STATUS\u NOT\u DEFINED.getStatusFlagValue()| StatusFlag.DOCUMENT_STATUS_MISSING_TID_DIR.getStatusFlagValue()
使用我的方法,这可以作为StatusFlag.DOCUMENT_STATUS_NOT DEFINED.flag | StatusFlag.DOCUMENT_STATUS_MISSING_TID_DIR.flag
使用静态导入,可以是DOCUMENT_STATUS_NOT DEFINED.flag | DOCUMENT STATUS_MISSING_TID_DIR flag.
使用序号可能会导致代码脆弱。只需重新组织枚举中的TEM将破坏一切,因为他将持久化到db。您的枚举还可以包含静态combine()、remove()和test()方法,以创建enum.StatusFlag.combine(DOCUMENT_STATUS_OK,DOCUMENT_STATUS_…)如果愿意,您还可以选择提供特定的值来绕过排序问题
作为瞬态,永远不会序列化其值。对于仅运行时的解决方案,这很好,但如果序列化到数据库,则应该避免。为什么给枚举值不好?因为它们已经有可以用来解决此问题的值。我相信OP需要与旧行为兼容的值。ordinal()
应始终被视为暂时性的,而不是序列化的,因为如果有人重新排序定义,兼容性将中断。依赖于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 final int flag;
StatusFlag() {
this.flag = 1 << this.ordinal();
}
}