Java枚举
枚举的理解
枚举,的好处就是,在代码输入的时候,可以明确知道可以输入哪些值。而常量,则无法限制。
优点1. 强制性约束
- 可以让调用者直观且明确的知道,可以传递哪些值
- 可以约束调用者,必须用指定的Enum。 (看例子)
例子:
public class ConstantTest {
// 使用常量的好处是简单。这个不用证明大家都明白
static class OrderConstant {
// 订单创建
private static final String ORDER_BUILDER = "01";
// 订单提交
private static final String ORDER_COMMIT = "05";
// 订单取消
private static final String ORDER_CANCEL = "98";
}
enum OrderEnum {
ORDER_BUILDER,ORDER_COMMIT,ORDER_CANCEL
}
@Data
static class Order {
private String orderNo;
private String status;
private OrderEnum orderEnumStatus;
private Date createDate;
}
public static void main(String[] args) {
test1();
test2();
}
// 优点1. 强制约束
public static void test1() {
Order order = new Order();
// 如果是常量,正常人是这么用的。
order.setStatus(OrderConstant.ORDER_BUILDER);
// 准备跑路的程序员可以这么用:可以不通过OrderConstant 进行填写。。有些人可能会瞎几把写代码
// 这样代码没有约束
order.setStatus("提桶跑路");
// 必须强制性天一个Enum类型。 这个就是优点。。
order.setOrderEnumStatus(OrderEnum.ORDER_BUILDER);
}
// 优点2. 可以使用==号,并且不需要做null校验
public static void test2() {
Order order = new Order();
order.setOrderEnumStatus(null);
if (order.getOrderEnumStatus() == OrderEnum.ORDER_BUILDER) {
System.out.println("订单创建");
}
if (order.getOrderEnumStatus() == null) {
System.out.println("null 你也敢传");
}
}
}
优点2 可以直接使用==
可以直接使用并且,不需要害怕空异常
// 优点2. 可以使用==号,并且不需要做null校验
public static void test2() {
Order order = new Order();
order.setOrderEnumStatus(null);
if (order.getOrderEnumStatus() == OrderEnum.ORDER_BUILDER) {
System.out.println("订单创建");
}
if (order.getOrderEnumStatus() == null) {
System.out.println("null 你也敢传");
}
}
优点3 可以用Switch
枚举具体API
枚举格式
修饰符 enum 枚举名称 {
实例1名称, 实例2名称...;
}
枚举的实质
- 枚举类实际是 Enum的子类
public abstract class Enum<E extends Enum<E>>
extends Object implements Comparable<E>, Serializable
-
Enum 继承了Object 所以,拥有Object 所有方法。
-
Enum可以实现接口。不能继承。
java 是单继承,所以enum不能再使用extends 。 但是可以实现其他接口。
/** * @author liuqh * @version 1.0 * @date 2022/4/26 */ public enum StatusEnum implements NameCodeEnum<String> { COMMIT("01", "提交"),TEMP("00", "草稿"),PAUSE("03", "暂停"); private String name; private String code; StatusEnum(String code, String name) { this.code = code; this.name = name; } @Override public String getName() { return name; } @Override public String getCode() { return code; } }
-
枚举字面量
例如:上面的COMMIT 字面量就是COMMIT字符串
-
枚举特有方法
// 获得Enum的类对象 类<E> getDeclaringClass(); // 重写了toString 方法。返回枚举常量名称 String toString(); // 通过枚举常量名称, 返回枚举 static <T extends Enum<T>> T valueOf(类<T> enumType, String name) // 返回枚举常量名称, 跟自定义的name没有半毛钱关系 public final String name(); // 返回枚举的顺序 public final int ordinal()
-
枚举构造器
咱们可以定义自己的构造器,但是枚举内部都会调用Enum的构造器存入
ordinal
与 枚举字面量name
protected Enum(String name, int ordinal) 唯一的构造函数。 程序员无法调用此构造函数。 它由编译器响应枚举类型声明发出的代码使用。 参数 name - - 此枚举常量的名称,它是用于声明它的标识符。 ordinal - - 这个枚举常数的序数(它在枚举声明中的位置,其中初始常数被分配为零的序数)。
通过接口简化枚举
通常业务经常会定义code 与 name, 例如: COMMIT(“01”, “提交”)。 01
是code , 提交
是name。
经常会有通过code
查找对应的枚举,或者通过name
查找对应的枚举。
问题案例
经常实现:
package com.moveo.dev.enums;
/**
* @author liuqh
* @version 1.0
* @date 2022/4/26
*/
public enum ErrorEnum {
PASSWD_ERROR("1003", "密码错误"),
USERNAME_ERROR("1004", "用户名错误"),
SYSTEM_ERROR("2001", "系统错误")
;
private final String code;
private final String name;
private ErrorEnum(String code, String name) {
this.code = code;
this.name = name;
}
public String getCode() {
return code;
}
public String getName() {
return name;
}
/**
* 通过code 返回枚举的中文值
* @author liuqh
* @param code
* @return String 空返回null
* @date 2022/4/26
**/
public static String getNameByCode(String code) {
ErrorEnum[] values = ErrorEnum.values();
for (ErrorEnum errorEnum : values) {
if (errorEnum.getCode().equals(code)) {
return errorEnum.getName();
}
}
// 未找到返回null
return null;
}
/**
* 通过code 返回枚举。 没找到返回null
* @author liuqh
* @param code
* @return ErrorEnum
* @date 2022/4/26
**/
public static ErrorEnum getEnumByCode(String code) {
ErrorEnum[] values = ErrorEnum.values();
for (ErrorEnum errorEnum : values) {
if (errorEnum.getCode().equals(code)) {
return errorEnum;
}
}
// 未找到返回null
return null;
}
}
问题是每个枚举类型,都需要写一遍
getEnumByCode
。造成大量的复制粘贴操作
解决案例
思路:枚举虽然不能继承,但是可以通过接口。使用工具类来完成操作。
接口:
/**
* 这里采用T泛型的原因是,有的枚举类并不需要枚举名且枚举code可能为String 或者Integer
*
*
* @author liuqh
* @date 2022/4/25
* @version 1.0
*/
public interface CodeEnum<T> {
/**
*获取枚举值
* @return
*/
T getCode();
}
/**
* 带有枚举值以及枚举名称的枚举接口(可以使用{@link EnumUtils} 中的方法)
*
* @author liuqh
* @version 1.0
* @date 2022/4/25
*/
public interface NameCodeEnum<T> extends CodeEnum<T> {
/**
* 获取枚举名称
*
* @author liuqh
* @return String
* @date 2022/4/25
**/
String getName();
}
实现类:
public enum StatusEnum implements NameCodeEnum<String> {
COMMIT("01", "提交"),TEMP("00", "草稿"),PAUSE("03", "暂停");
private final String name;
private final String code;
StatusEnum(String code, String name) {
this.code = code;
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public String getCode() {
return code;
}
}
枚举工具类:
package com.moveo.dev.enums;
import org.apache.commons.lang3.StringUtils;
/**
* @author liuqh
* @version 1.0
* @date 2022/4/25
*/
public class EnumUtils {
/**
* 判断枚举值是否存在于指定枚举数组中
*
* @param enums 枚举数组
* @param code 枚举code
* @return 是否存在
*/
public static <T> boolean isExist(CodeEnum<T>[] enums, T code) {
if (code == null) {
return false;
}
for (CodeEnum<T> e : enums) {
if (code.equals(e.getCode())) {
return true;
}
}
return false;
}
/**
* 判断枚举值是否存与指定枚举类中
*
* @param enumClass 枚举类
* @param code 枚举code
* @param <E> 枚举类型
* @param <V> 值类型
* @return true:存在
*/
@SuppressWarnings("unchecked")
public static <E extends Enum<? extends CodeEnum<V>>, V> boolean isExist(Class<E> enumClass, V code) {
for (Enum<? extends CodeEnum<V>> e : enumClass.getEnumConstants()) {
if (((CodeEnum<V>) e).getCode().equals(code)) {
return true;
}
}
return false;
}
/**
* 根据枚举值获取其对应的名字
*
* @param enums 枚举列表
* @param code 枚举值
* @return 枚举名称
*/
public static <T> String getNameByCode(NameCodeEnum<T>[] enums, T code) {
if (code == null) {
return null;
}
for (NameCodeEnum<T> e : enums) {
if (code.equals(e.getCode())) {
return e.getName();
}
}
return null;
}
/**
* 根据枚举名称获取对应的枚举code
*
* @param enums 枚举列表
* @param name 枚举名
* @return 枚举code
*/
public static <T> T getCodeByName(NameCodeEnum<T>[] enums, String name) {
if (StringUtils.isEmpty(name)) {
return null;
}
for (NameCodeEnum<T> e : enums) {
if (name.equals(e.getName())) {
return e.getCode();
}
}
return null;
}
/**
* 根据枚举值获取对应的枚举对象
*
* @param enums 枚举列表
* @return 枚举对象
*/
@SuppressWarnings("unchecked")
public static <E extends Enum<? extends CodeEnum<V>>, V> E getEnumByCode(E[] enums, V code) {
for (E e : enums) {
if (((CodeEnum<V>) e).getCode().equals(code)) {
return e;
}
}
return null;
}
/**
* 根据枚举值获取对应的枚举对象
*
* @param enumClass 枚举class
* @return 枚举对象
*/
public static <E extends Enum<? extends CodeEnum<V>>, V> E getEnumByCode(Class<E> enumClass, V Code) {
return getEnumByCode(enumClass.getEnumConstants(), Code);
}
}