Java枚举的几个用法探索

枚举是java 5之后添加的一个重要特性,这个特性可以让java程序员写出更优雅、合理的代码。

1. 枚举基本用法

/**
 * 消息的类型,是请求还是回应
 * @author leo
 */
public enum ReqOrRes {
    req,res;
}

这应该是枚举最简单的用法了,即使是这样简单的使用,也能给我们带来便利,

ReqOrRes.req.name() == "req"

ReqOrRes.valueOf("req") == ReqOrRes.req , 通过name 和valueOf这两个枚举内置的方法,可以方便的将枚举在字符串和枚举之间转化。

2. 枚举中带方法,将处理逻辑写在枚举类中,将调用框架和业务逻辑分离

/**
 * 列举调度server与slave之间的所有通信报文类型,每个类型的具体描述见com.zdcin.msgbean包,<br>
 * 每种消息有两个方法供外部调用,根据消息是在哪里产生,对应一方需实现processReq方法,产生消息的一方需要实现processRes方法。
 * @author leo
 *
 */
public enum MsgType {// MsgType有两个主要方法,processReq,和processRes, 用于完成处理各种类型的请求消息和响应消息的逻辑

    /** slave获取clientid 
     * 
     */
    GET_CLIENT_ID {
        @Override
        public void processReq(String json) {// 覆盖父类MsgType中的同名方法,processRes方法在另外一端实现,这个方法的意思是处理请求,并发回响应信息
            GET_CLIENT_ID_req req = MsgUtils.getGson().fromJson(json, GET_CLIENT_ID_req.class);
            GET_CLIENT_ID_res res = new GET_CLIENT_ID_res(req);
            。。。。。
            //用clientid做key找到连接,把消息发回去
            Client.sendMsgToSlave(res.clientId, res);
        }
    },
    /** 已经分配过id的slave每次连接到server都要用该命令通知server建立连接 */
    CONNECT {
        @Override
        public void processReq(String json) {
            CONNECT_req req =  MsgUtils.getGson().fromJson(json, CONNECT_req.class);
            。。。。。
        }
    },

    /** 任务指定 */
    TASK_ASSIGN {
        @Override
        public void processRes(String json) {
            //不用实现
        }
    },
/** 心跳 */
    HEART_BEAT {
        
        @Override
        public void processReq(String json) {
            HEART_BEAT_req req = MsgUtils.getGson().fromJson(json, HEART_BEAT_req.class);
            if ("00X".equalsIgnoreCase(req.meta.clientId)) {
                return;
            }
            HEART_BEAT_res res = new HEART_BEAT_res(req);
            Client.sendMsgToSlave(req.meta.clientId, res);
        }

        @Override
        public void processRes(String json) {
            //接收就行,不用处理
        }
    };
//------------ 到这里,枚举类型定义结束了,下边是各个枚举都可以用的公共方法
    /**
     * 消息有在调度server产生的,又在slave上产生的,
     * 在调度器上产生的消息,slave需要实现processReq方法,并且slave需要返回res消息给调度器,调度器就需要实现processRes方法;
     * 如果消息是在slave上产生(如主动上报状态的消息),上述流程就反过来。
     * @param json
     */
    public void processReq(String json){//父类定义的方法
        throw new RuntimeException(this.name() + ".processReq() method not implement.");
    }

    /**
     * 消息有在调度server产生的,又在slave上产生的,
     * 在调度器上产生的消息,slave需要实现processReq方法,并且slave需要返回res消息给调度器,调度器就需要实现processRes方法;
     * 如果消息是在slave上产生(如主动上报状态的消息),上述流程就反过来。
     * @param json
     */
    public void processRes(String json){//父类定义的方法
        throw new RuntimeException(this.name() + ".processRes() method not implement.");
    }
}

上述枚举类的作用是定义消息类型,同时在processReq和processRes方法中实现处理各种请求和响应消息的逻辑,这只是消息一端的代码,一般消息中processReq和processRes只需要出现一次,在消息的另一段,对称的实现缺少的那个方法就可以。

这个枚举类用起来大概是这个样子的:

public class ReciveWork implements Runnable {

    private String json;
    private Meta meta;

    public ReciveWork(Meta meta, String json) {
        this.meta = meta;
        this.json = json;
    }

    @Override
    public void run() {
        try {
            switch (meta.reqOrRes) {
            case req://根据消息是请求类型还是响应类型,来决定是调用 processReq方法还是 processRes方法。
                meta.msgType.processReq(json);// meta.msgType 就是MsgType中的CONNECT, GET_CLIENT_ID等枚举,这里不确定是哪一个,但是可以确定他们会有processReq方法,这样就可以优雅的动态分发消息给合适的处理者了 
                break;

            case res:
             // 检查重发队列,如果在队列中,取消重发
                Client.notifyToStopRetry(meta);
                // 先检查有对方有没有处理错误,有的话,直接记录日志,不调用后续的处理方法了, 相关的错误恢复工作可以在定时任务中进行。
                BaseResMsg baseres = MsgUtils.getGson().fromJson(json, BaseResMsg.class);
                if (baseres.errCode != 0) {
                    Main.log.error("error in " + baseres.meta.msgType.name() + "_res,  [errCode, errMsg]=["
                            + baseres.errCode + ", " + baseres.errMsg + "]");
                } else {
                    meta.msgType.processRes(json);
                }
                
                break;
            }
        } catch (Exception e) {
            Main.log.error(
                    "error in " + meta.msgType.name() + ".process" + meta.reqOrRes.name() + ", " + e.getMessage(), e);
        }
    }

}

worker类实现了Runnable接口,可以放在线程池里,多线程并行分发处理。

现在看,效果还不错吧,如果不用枚举,估计少不了一堆if else分支判断,或者是基于抽象类的模式,每个消息类型写一个子类,去做实现,应该不会比这简单。

3. 带数值可叠加的枚举

/**
 * @author leo
 */
public enum AppMonitorType {
    /**读权限*/
    read(1),
    /** 写权限*/
    write(2),
    /** 执行权限*/
    exec(4),
    /** 特殊权限*/
    spic(8);
    
    private int bitValue;
    private AppMonitorType(int v) {//私有的构造方法,只能类初始化时使用,如exec(4),
        this.bitValue = v;
    }
    public int getBitValue() {// 返回枚举对应的整形值,如 exec(4) 中的4
        return this.bitValue;
    }
    
    //将数值转换成权限列表, 这个方法是静态的, 直接AppMonitoryType.parseBitValue()
    public static List<AppMonitorType> parseBitValue(int bitValue) {
        if (bitValue <= 0) {
            return null;
        }
        List<AppMonitorType> list = new ArrayList<AppMonitorType>();
        for (AppMonitorType type : AppMonitorType.values()) {
            if ((type.bitValue & bitValue) == type.bitValue) {
                list.add(type);
            }
        }
        return list;
    }
    // 将权限列表转换成数值, 注意这个方法是静态的,用的时候 直接AppMonitorType.getBitValue()
    public static int getBitValue(List<AppMonitorType> list) {
        int i = 0;
        for (AppMonitorType type : list) {
            i |= type.bitValue;
        }
        return i;
    }
}

你有关于枚举使用的一些奇妙的想法吗,欢迎一起讨论。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容