java中不太常见的东西(1) - 枚举enum

引言

之所以我想总结一下java中不太用的东西,是因为我再研究每个版本jdk中,发现有些内容“热火朝天”,但是有些东西却“门可罗雀”。比如说jdk1.5中新增了泛型,强化for循环和枚举等,但是前两者已经被各位熟知了,但是枚举在日常开发中都不太会用的。在本篇博文中,我会详细介绍enum的使用方式,同时比较常量与enum的优劣。笔者目前整理的一些blog针对面试都是超高频出现的。大家可以点击链接:http://blog.csdn.net/u012403290

技术点

在jdk1.5中新增的一个引用类型,用enum表示,用以表示一组固定常量,比如说用以标识订单状态:未付款,已付款,已发货,已收货等等。enum**没有可以访问的构造器(禁止使用public),枚举类型是final的,因此枚举类型是实例受控的,也就是安全的。同时,enum还支持添加任意的方法和域**、可以实现接口、而且还提供了Object类的所有方法(意思就是说enum默认继承了Object,可以使用equals和toString的Object的方法)。

场景1-int/String枚举模式

int/String枚举模式是枚举最简单的应用。在电子商务系统中,必然存在着订单状态表示,假如说我用:1表示为付款,2表示已付款。

那么以下有三位程序员各自的实现:

程序员1直接用数值表示状态:

package com.brickworkers;
/**
 * @author Brickworker
 * Date:2017年4月11日下午12:30:18 
 * 关于类EnumTest.java的描述:枚举类例子
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public class EnumTest {

    //程序员1直接用数值表示状态
    //用户支付成功后,修改订单状态
    order.setStatus(2);
}

这个是最恶心的,我称这种代码为sick code,且不说别的程序员看不看得懂,我相信程序员1在过一段时间回来自己看都看不懂什么意思。所以千万别这么做,在日常书写代码中要保证在类中不要出现直接的常量,不然你会被嘲笑的。

程序员2先用CommonConstant用以存储状态:

package com.brickworkers;

public class CommenConstant {

    public static final int NOT_PAY = 1;

    public static final int IS_PAY = 2;

}

然后再要使用的时候把静态常量引入:

package com.brickworkers;
/**
 * @author Brickworker
 * Date:2017年4月11日下午12:30:18 
 * 关于类EnumTest.java的描述:枚举类例子
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public class EnumTest {

    //程序员2用静态常量引入订单状态
    order.setStatus(CommenConstant.IS_PAY);
}

这个应该是我们实际开发中最常用的方式了,大家可以把所有的静态变量整合到一起,每次用的时候引用一下。的确非常的便捷,博主说实话也是用这种方式,但是很明显不够优雅,个人觉得原因有如下3点:①所有的常量混在一个类中,这个类的功能只是为了提供这些常量,这个类变成并没有严格的意义;②各种常量混在一个类中,层次感和逻辑并不是非常清晰,你还要保证不能重名;③不能很好的打印出状态,不能很直观的看出什么代表了什么(1代表未付款)。

程序员3通过用枚举类进行实现:

package com.brickworkers;

/**
 * @author Brickworker
 * Date:2017年4月11日下午12:53:32 
 * 关于类OrderStatus.java的描述:枚举类
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public enum OrderStatus {

    NOT_PAY(1), IS_PAY(2);

    private final int val;
    private OrderStatus(int val) {
        this.val = val;
    }

    @Override
    public String toString() {
        return "名称:"+this.name()+"  对应值:"+this.val;
    }
}

在这种表述中,用enum枚举表示了未付款和已付款,同时它还重写了Object的toString方法。那么和上面一样在使用的时候就是:

package com.brickworkers;
/**
 * @author Brickworker
 * Date:2017年4月11日下午12:30:18 
 * 关于类EnumTest.java的描述:枚举类例子
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public class EnumTest {


    //程序员3用枚举来实现静态常量
    order.setStatus(OrderStatus.IS_PAY.val);

}

或许如此看来,很多小伙伴觉得很臃肿,何必这么麻烦?是的,因为这里一定要用int类型来表示订单状态,即使用了枚举也必须转化到int为止,但是如果你一开始设计的时候就用枚举来表示的话,那么就可以避免这么麻烦了,在上述的例子中就可以不用用构造函数对每个枚举常量进行处理,也不需要用val这个成员变量。大家都是表示订单状态,用int和用String其实没什么很大的差别

同时,枚举可以直接通过toString的重写来打印状态,比如说下面的代码:

package com.brickworkers;
/**
 * @author Brickworker
 * Date:2017年4月11日下午12:30:18 
 * 关于类EnumTest.java的描述:枚举类例子
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public class EnumTest {
    public static void main(String[] args) {
        for (OrderStatus os :OrderStatus.values()) {
            System.out.println(os);
        }
    }
}

//打印结果:
//名称:NOT_PAY  对应值:1
//名称:IS_PAY  对应值:2

是不是有种焕然一新的感觉呢?

场景2 - 具有一定能力的枚举

获取在场景1中,枚举并没有给你带来很大的吸引力。但是如果枚举有一些更强大的功能呢?考虑第二种情况,比如说有这么一种情况,比如说你给自己制定了一个学习生活计划,周一到周三,这个时候就可以用枚举类来实现了,而且非常适合。有两种实现方式:

程序员用了enum独特的switch-case来实现:

package com.brickworkers;

public enum MySchedule {
    MONDAY, TUESDAY, WEDNESDAY;

    String plan(){
        switch (this) {
        case MONDAY:
            return "玩";
        case TUESDAY:
            return "玩";
        case WEDNESDAY:
            return "玩";
        default:
            return "学习";
        }
    }

}

今天是周一,于是我去查看了我的日程安排表:

package com.brickworkers;
/**
 * @author Brickworker
 * Date:2017年4月11日下午12:30:18 
 * 关于类EnumTest.java的描述:枚举类例子
 * Copyright (c) 2017, brcikworker All Rights Reserved.
 */
public class EnumTest {

    public static void main(String[] args) {
        System.out.println("今天周一,我看了下我的安排,我发现我周一要做的是:"+MySchedule.MONDAY.plan());
    }
}

//输出结果:
//今天周一,我看了下我的安排,我发现我周一要做的是:玩

是不是觉得enum也挺好玩的呢?仔细观察上面的代码,你会发现根本不会跑到default的语句块,因为枚举没有除却周一,周二,周三之外的。那么加入这个时候,我加入了周四呢?但是我忘记指定周四要干嘛了,那么不好意思,按照你的日常表来说,你没有制定你新加入的枚举常量,那么你只能默认“学习”了。其实还有更好的实现方式,请参考下面的实现代码:

package com.brickworkers;

public enum MySchedule {
    MONDAY {
        @Override
        String plan() {
            return "玩";
        }
    }, 
    TUESDAY {
        @Override
        String plan() {
            return "玩";
        }
    }, 
    WEDNESDAY {
        @Override
        String plan() {
            return "玩";
        }
    },

    THURSDAY {//新加入了一个星期四,被强制要求实现plan方法
        @Override
        String plan() {
            return "玩";
        }
    };

    abstract String plan();
}

把plan方法写成抽象的,那么你没定义一个枚举常量就会被强制要求你实现这个方法,这称为:特定于常量的方法实现。这个方式可以防止你新增一个常量而忘记添加它的特定属性,enum会强制你去实现抽象的方法。比如说上面我新增了一个星期四,那么我必须实现它的plan方法。 是不是觉得enum更有意思了呢?其实枚举的功能可以按照你自己的想法进行扩展,比如说上面日常安排的枚举,可以重写toString来打印出一张完美的日程表。

场景3 - 枚举里面嵌套枚举(策略枚举)

类当中嵌套类,我们已经看过很多,在枚举中我们可以实现嵌套枚举。继续上面的例子,为了防止你往你的日程表中添加一天的时候忘记标注你要干嘛,上面我们用abstract抽象方法解决了这个问题。在此基础上,我们要构建一个更加优雅的方式,我们把要做的事情包装成一个枚举,然后把这个枚举内嵌到枚举内部(和静态内部类功能的表示都很相似),接着构造一个构造函数,入参就是你要选择安排的方式,具体的代码实现如下:

package com.brickworkers;

public enum MySchedule {
    MONDAY(MyActivity.SLEEP), TUESDAY(MyActivity.SLEEP), WEDNESDAY(MyActivity.SLEEP);

    private MySchedule(MyActivity activity) {//建立一个机遇策略枚举的一个构造函数,强制每个添加的日期都必须在活动列表中选择一个活动
    }
    private enum MyActivity{//活动列表
        SLEEP {
            @Override
            String paly() {
                return "睡觉";
            }
        }, PLAY_BALL {
            @Override
            String paly() {
                return "打球";
            }
        }, STUDY {
            @Override
            String paly() {
                return "学习";
            }
        };
        abstract String paly();
    }

}

简单解释一下上面的代码,内嵌的MyActivity表示你可以选择的活动列表,在外部建立一个基于内嵌枚举的构造函数,保证每次你往日程中添加一天就强制要求你去选择一种活动。有的人会思考,我觉得这个方法与上面提供方法效果一样啊,效果虽然差不多,但是内嵌枚举这种模式,可以使得更容易让人理解,并且你要的活动都给你包装好了,你只要负责选择就好。

尾记

枚举就介绍这么多,其实枚举还有一些功能没有介绍(实现接口,用接口组织枚举等),可能我举得例子没有那么足够吸引你,但是希望在日常开发中多思考能否用枚举来实现,比如说要建立用户权限关系的时候(管理员,用户,超级用户,超级管理员),请考虑一下枚举,或许你会发现新大陆。

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页