JDK1.5之前的枚举

在JDK1.5之前是没有enum这个关键字的,那么那个时代是怎么实现枚举的呢?主要是通过私有化构造器,然后在类里面创建静态final的对象,在类的外面通过 ** 类名.对象名 ** 来使用枚举的,如下:

class Season{
    String name;
    String description;

    private Season(String name, String description) {
        this.name = name;
        this.description = description;
    }
    public static final Season SPRING = new Season("Spring", "春天");
    public static final Season SUMMER = new Season("Summer", "夏天");
    public static final Season AUTUMN = new Season("Autumn", "秋天");
    public static final Season WINTER = new Season("Winter", "冬天");

    public String getName() {
        return name;
    }

    public String getDescription() {
        return description;
    }

    @Override
    public String toString() {
        return "Season{" +
                "name='" + name + '\'' +
                ", description='" + description + '\'' +
                '}';
    }
}

// 测试方法
@Test
public void test1(){
    Season Spring = Season.SPRING;
    Season Summer = Season.SUMMER;
    Season Autumn = Season.AUTUMN;
    Season Winter = Season.WINTER;
    System.out.println(Spring);
    System.out.println(Spring.getName()+" "+Spring.getDescription());
    /**
     * 打印结果:
     * Season{name='Spring', description='春天'}
     * Spring 春天
     */
}

JDK1.5之后的枚举

在JDK1.5之后,出现了enum关键字,它的作用就是简化前面使用枚举的繁琐,解放程序员的双手的:

enum Season1{
    SPRING("Spring","春天"),
    SUMMER("Summer", "夏天"),
    AUTUMN("Autumn", "秋天"),
    WINTER("Winter", "冬天");

    String name;
    String description;

    Season1(String name, String description) {
        this.name = name;
        this.description = description;
    }

    public String getName() {
        return name;
    }

    public String getDescription() {
        return description;
    }

    @Override
    public String toString() {
        return "Season{" +
                "name='" + name + '\'' +
                ", description='" + description + '\'' +
                '}';
    }
}

@Test
public void test2(){
    Season1 Spring = Season1.SPRING;
    Season1 Summer = Season1.SUMMER;
    System.out.println(Spring);
    Season1[] season1s = Season1.values();
    for(Season1 season1: season1s){
        System.out.println(season1 + " ----------"+season1.getName()+"--"+season1.getDescription());
    }
    /**
     * 打印结果:
     * Season{name='Spring', description='春天'}
     * Season{name='Spring', description='春天'} ----------Spring--春天
     * Season{name='Summer', description='夏天'} ----------Summer--夏天
     * Season{name='Autumn', description='秋天'} ----------Autumn--秋天
     * Season{name='Winter', description='冬天'} ----------Winter--冬天
     */
}

使用enum之后只是有几个点要注意一下的:

  1. 对象名紧跟在类名后面
  2. 对象名后面的参数其实就是传向构造器中的参数

在我看来,其实enum就是一个类,只是这个类定义的时候与其他类不同而已,它的使用方法也略微有点不同,它有一些自己特有的方法,比如values(),valueOf(String str)等。同样,作为一个类,它也能实现接口,比如:

interface EnumInterface{
    void show();
}
enum Season1 implements EnumInterface{
    SPRING("Spring","春天"){
        @Override
        public void show(){
            System.out.println("Spring show()");
        }
    },
    SUMMER("Summer", "夏天"){
        @Override
        public void show(){
            System.out.println("Summer show()");
        }
    },
    AUTUMN("Autumn", "秋天"){
        @Override
        public void show(){
            System.out.println("Autumn show()");
        }
    },
    WINTER("Winter", "冬天"){
        @Override
        public void show(){
            System.out.println("Winter show()");
        }
    };

    String name;
    String description;

    Season1(String name, String description) {
        this.name = name;
        this.description = description;
    }

    public String getName() {
        return name;
    }

    public String getDescription() {
        return description;
    }

    @Override
    public String toString() {
        return "Season{" +
                "name='" + name + '\'' +
                ", description='" + description + '\'' +
                '}';
    }
}
@Test
public void test2(){
    Season1 Spring = Season1.SPRING;
    Season1 Summer = Season1.SUMMER;
    System.out.println(Spring);
    Season1[] season1s = Season1.values();
    for(Season1 season1: season1s){
        System.out.println(season1 + " ----------"+season1.getName()+"--"+season1.getDescription());
        season1.show();
    }
    /**
	 * 打印结果:
     * Season{name='Spring', description='春天'}
     * Season{name='Spring', description='春天'} ----------Spring--春天
     * Spring show()
     * Season{name='Summer', description='夏天'} ----------Summer--夏天
     * Summer show()
     * Season{name='Autumn', description='秋天'} ----------Autumn--秋天
     * Autumn show()
     * Season{name='Winter', description='冬天'} ----------Winter--冬天
     * Winter show()
     */
}

此时,在每个对象中重写show()方法,可以使得每个对象都具有不同的行为(实现策略模式),倘若只是在类中重写show()方法,则所有的对象都只是具有一个相同的行为而已。

小结

枚举并非什么神奇的东西,它只不过是帮助我们简化了一些操作而已,比如我们之前写一些枚举状态之类的东西,可能是这样:

class Status{
    public static final String NEW = "NEW";
    public static final String FINISHED = "FINISHED";
}

用了枚举之后就是这样的,在枚举类的外部还可以使用values()来获取一组Status的对象的数组,极大的方便了我们对状态的遍历。

enum Status1{
    NEW("NEW"),
    FINISHED("FINISHED");

    private String str;
    private Status1(String str){
        this.str = str;
    }
}

Enum源码分析

假设有这么一个简单的枚举:

public enum EnumTest {
    SOMEONE,

    ANOTHER_ONE
}

首先对其进行编译,然后使用命令javap -verbose EnumTest.class对其反编译,得到如下部分:

public final class com.lin.EnumTest extends java.lang.Enum<com.lin.EnumTest>

所以,对于Java来说,枚举是一个语法糖。

使用策略枚举代替策略模式

普通写法:

enum Operation {

    PLUS,MINUS,TIMES,DIVIDE;

    double apply(double x,double y){
        switch (this){
            case PLUS:return x + y;
            case MINUS:return x - y;
            case TIMES:return x * y;
            case DIVIDE:return x / y;
        }
        throw new AssertionError("Unknow op: " + this);
    }
}

使用策略枚举使得代码更加健壮、扩展性更好:

enum Operation {

    PLUS("+") {
        @Override
        double apply(double x, double y) {
            return x + y;
        }
    },
    MINUS("-") {
        @Override
        double apply(double x, double y) {
            return x - y;
        }
    },
    TIMES("*") {
        @Override
        double apply(double x, double y) {
            return x * y;
        }
    },
    DIVIDE("/") {
        @Override
        double apply(double x, double y) {
            return x / y;
        }
    };

    private final String symbol;

    Operation(String symbol) {
        this.symbol = symbol;
    }

    @Override
    public String toString() {
        return this.symbol;
    }

    abstract double apply(double x, double y);
}

优雅的嵌套枚举策略模式:

enum PayrollDay {

    MonDAY(PayType.WEEKDAY),
    WEEKDAY(PayType.WEEKDAY),
    TUESDAY(PayType.WEEKDAY),
    WENDESDAY(PayType.WEEKDAY),
    THURSDAY(PayType.WEEKDAY),
    FRIDAY(PayType.WEEKDAY),
    SATURDAY(PayType.WEEKEND),
    SUNDAY(PayType.WEEKEND);

    private PayType payType;

    PayrollDay(PayType payType) {
        this.payType = payType;
    }

    double pay(double hoursWorked, double payRate) {
        return payType.pay(hoursWorked, payRate);
    }

    //这里是嵌套枚举
    private enum PayType {

        WEEKDAY {
            @Override
            double overtimePay(double hrs, double payRate) {
                return 0;
            }
        }, WEEKEND {
            @Override
            double overtimePay(double hrs, double payRate) {
                return 0;
            }
        };

        private static final int HOURS_PRE_SHIFT = 8;

        abstract double overtimePay(double hrs, double payRate);

        double pay(double hoursWorked, double payRate) {
            double basePay = hoursWorked * payRate;
            return basePay + overtimePay(hoursWorked, payRate);
        }
    }
}