一般而言,Java 的实体(Bean/Pojo)很少涉及“枚举(Enum)”类型,例如男女,分别用 int 表示 1= 男/2=女 就好了。Bean 写法如下。
private Integer sex; public Integer getSex() { reutrn sex; } public void setSex(Integer sex) { this.sex = sex; }
这是最正常不过了。但是,为了提高语义程度,我们希望把枚举的概念利用起来,那样程序代码的好处是更清晰了。使用枚举类型,是这样子的。
private Gender gender; public Gender getGender() { return gender; } public void setGender(Gender gender) { this.gender = gender; }
必须承认,为了换取可读性和语义性,那是有代价的,它会适当增加你的代码复杂度。但实际上,笔者觉得这是一个更细致深入的建模过程,抛弃掉以往那种定义常量的方式。
public static interface Gender { public static final int MALE = 1; public static final int FEMALE= 2; }
而采用 Java Enum:
import com.ajaxjs.framework.IntegerValueEnum; public enum Gender implements IntegerValueEnum { UNKNOW, MALE, FEMALE; @Override public Integer getValue() { return ordinal(); } }序列化与反序列化
这仅仅是 Java 层面的,如果涉及“序列化与反序列化”,也就是考虑:数据库与 Json 两处地方,该如何保存枚举呢?
首先数据库仍旧不变,还是保持 int/string。虽然 MySQL 支持 enum 类型字段,但为跨数据库而言,不考虑特殊类型的字段去使用。存储方面照旧,而问题在于读取的时候,怎么把 int/string 变为 Java 的枚举类型。
其二,输出 Json,怎么把 Java 的枚举类型变为 Json 输出呢?
综上所述,解决的一个思路就是,凡是遇到实体中的枚举类型,就作适当的转换,跟以前 Integer/String/Boolean 类型针对性处理的手法一样。当然那需要我们额外的 *** 作,也就是前面所说的“代价”,你要在你的 ORM 框架(如 Mybatis)或者 Json 序列库(Jackson)中,当遍历 Bean 各字段时候,处理好 Enum 类型。
虽然不同框架的遍历切入的方法不一样,要看它具体如何开放出来,但是针对枚举的转换却是可以共性的。这点我们最后再讲。
如何定义枚举既然我们说要识别枚举、处理枚举,那么需要将枚举类用一个接口简单标识一下,说明它是为 Bean 服务。我们知道,Java 中的枚举可以实现接口(但不能继承)。于是设计一个 baseEnmu,表示,POJO 中可以读取的枚举,以及可以获取枚举值,
import java.io.Serializable; public interface baseEnmu { Serializable getValue(); }
其实每个枚举类都继承 Enum——要看它是不是枚举,可以简单用 instanceOf Enum 一下,但为了更方便的识别,获取 value 值,最好还是设置一个 baseEnmu 接口。
关于枚举值,就是保存到数据库或者输出到 Json 的那个值。不是方便人类阅读的(例如 0/1……),——在 Java 中使用枚举才是方便阅读的(例如 Male/Female……)。所以它肯定可以返回一个值,即 Serializable getValue();。Serializable 类型的用意是兼容了 Number/String 的值。
如何安排值?一般是 MALE(1), FEMALE(2),甚至不写,用系统索引序号也可以。至于 String 类型也行,看你需求。使用系统索引序号有个问题,下面会讲。
当然,除了值你还可以在枚举中加入丰富的内容,相当于横向扩充说明,这就有点像 Java 程序里面的数据字典了,也可以被其他代码所引用。例如这个 Status 笔者加入的 desc 说明。
public enum Status implements IntegerValueEnum, DescEnum { ONLINE(1, "上线"), OFFLINE(0, "下线"), DELETED(-1, "已删除"); Status(Integer value, String desc) { this.value = value; this.desc = desc; } private Integer value; @Override public Integer getValue() { return value; } private String desc; @Override public String getDesc() { return desc; } @Override public boolean isUsingOrdinal() { return false; } }枚举的类型
一般我们都是用 int 即可,但不排除用字符串的。是 int 还是 String 我们要让程序知道,于是派生两个接口 IntegerValueEnum 和 StringValueEnum。
public interface IntegerValueEnum extends baseEnmu { public Integer getValue(); default boolean isUsingOrdinal() { return true; } }
public interface StringValueEnum extends baseEnmu { String getValue(); }
Enum 的 ordinal() 能返回枚举在数组中的索引,这个允许我们无须定义枚举值,按照枚举出现的顺序就好了。但它是从零开始算的,——万一我常量没有 0,而是从 1 开始算的——那就对不上了。于是我们提供一个标识,说明使用了 ordinal() 机制。
default boolean isUsingOrdinal() { return true; }
默认是使用的。如果你自己定义 value,那么就要把这个值设为 false。
枚举的转换前面说到序列化与反序列化的问题,这里谈谈具体如何解决。首先说说 Json 输出的,很简单,就是遇到枚举读取其值即可。
如截图,在不同类型的转换中,判断是否 baseEnum,然后转换即可,——一句两句代码的事情。如下图所示,是返回的 Json。
接着是,如何从数据库的值,转换到 Enum 呢?这个相对比较“烧脑”但其实也不难。笔者直接贴代码好了。
@SuppressWarnings({ "unchecked", "rawtypes" }) private static Object dbValue2Enum(Class> propertyType, Object _value) { if (_value != null) { boolean isNumber = _value instanceof Number, isString = _value instanceof String; // 写在这里不用在 for 里面每次判断 for (Object e : propertyType.getEnumConstants()) { if (isNumber) { IntegerValueEnum _e = (IntegerValueEnum) e; Integer _int = ((Integer) _value) - (_e.isUsingOrdinal() ? 1 : 0); if (_int == _e.getValue()) { return e; } } else if (isString) return Enum.valueOf((Class) propertyType, _value + ""); } } return null; }
这里笔者自研的 ORM 框架,在遍历数据库字段转换到 Java Bean 时候,调用 dbValue2Enum()。
Java 反射很方便的提供了 isEnum() 和 getEnumConstants() 使得可以让输入值与枚举值匹配上,——不然我真不知道该如何处理,使用注解说明哪个枚举类?太复杂了恐怕得不偿失。
最后希望你会喜欢这套机制,或者给你带来一点启发。不足之处,敬请提出!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)