Java基础之Enum类型

124 阅读6分钟

对于前端做前端的我来说,对于枚举其实并不是那么陌生,对于一些不确定的值给出一些可能性,比如switch...case这种,就是一个简单的枚举,但是这些枚举只是概念上的,javascript中没有一个明确的类型作为其枚举类型,但是在Java中可以对于枚举的充分利用。

在Java中枚举是一个被命名的整型常数的集合,用于声明一组带标识符的常数。类似这种当一个变量有几种固定可能的取值时,就可以将它定义为枚举类型。

举个例子在程序开发过程中想要表示四季的话,可能会用1,2,3,4分别代表春夏秋冬四季。Javascript可以用对象的形式表示,但是在Java中可以使用枚举轻松实现。

{
    1: "春季"2: "夏季"3: "秋季"4: "冬季"
}

那么在Java中如何实现同样的效果呢?首先定义一个枚举类型:

public enum SeasonEnum {
    SPRING,SUMMER,FALL,WINTER
}

通过上述代码轻松的创建了一个枚举类。当定义好枚举类之后如何查看枚举类里面的值呢?枚举类包含values的方法。会返回当前枚举类中所有的value值。

for(SeasonEnum SeasonEnum : SeasonEnum.values()){
    System.out.println(SeasonEnum);
}

虽然输出了value的值,但是想要实现其值代表的是春夏秋冬的话还是不行的,这种方式最简单的方式则是使用熟知的switch...case。

SeasonEnum SeasonEnum = SeasonEnum.SPRING;
switch (SeasonEnum){
    case SPRING:
        System.out.println("春");
        break;
    case SUMMER:
        System.out.println("夏");
        break;
    case FALL:
        System.out.println("秋");
        break;
    case WINTER:
        System.out.println("冬");
        break;
    default:
        System.out.println("无");
        break;
}

使用switch...case虽然实现了对应的效果但是这样看来,好像对于代码的维护并不能起到什么好的效果。并且并没有通过固定的值取到所代表的值。当然不仅如此,可以通过枚举中内置的一些方法进行操作。

Enum类的常用方法

方法名称描述
values以数组形式返回枚举类型的所有成员
valueOf将普通字符串转换为枚举实例
compareTo比较两个枚举成员在定义时的顺序
ordinal获取枚举成员的索引位置
SeasonEnum.FALL.compareTo(SeasonEnum.WINTER);
SeasonEnum.FALL.name();
SeasonEnum.WINTER.toString();
SeasonEnum.WINTER.ordinal();

可以通过上述方法获知枚举中的信息,当我们已知枚举中一个值的字符串信息的时候通过该字符串获取到对应的枚举的值。

String enumName = "WINTER";
SeasonEnum SeasonEnum = SeasonEnum.valueOf(SeasonEnum.class, enumName);
SeasonEnum.name = "春天";
System.out.println(SeasonEnum + " 是 " +SeasonEnum.name);

通过valueOf的方法获取到string对应的枚举类,并为查找到的枚举值的name赋值为春天。虽然看起来是对的但是还是感觉哪里怪怪的说不出来的感觉。

仔细观察可以看出,枚举的name是后赋值的,是在程序中后指定的这个对应的枚举值为春天,如果能像对象一样通过枚举值直接获取到name。

// 枚举
public enum SeasonEnum {
    SPRING("春"),
    SUMMER("夏"),
    FALL("秋"),
    WINTER("冬");
    
    public String name;

    SeasonEnum(String name){
        setName(name);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

};

对枚举类进行改造,为枚举类添加了对应的值,并且添加了构造方法,这里需要注意的是必须添加构造方法和属性才可以正常获取到的。

String enumName = "WINTER";
SeasonEnum seasonEnum = SeasonEnum.valueOf(SeasonEnum.class, enumName);
System.out.println(seasonEnum.getName());

在上述代码中给其添加了getName的方法,也可以为其添加其他的方法进行调用。

public enum SeasonEnum {
    SPRING("春"),
    SUMMER("夏"),
    FALL("秋"),
    WINTER("冬");

    public String name;

    SeasonEnum(String name){
        setName(name);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void info(){
        System.out.println("默认info方法");
    }
}

在枚举类中添加了一个默认的info方法,当获取到对应的枚举对象后就可以直接调用对应的方法了。

String enumName = "WINTER";
SeasonEnum seasonEnum = SeasonEnum.valueOf(SeasonEnum.class, enumName);
System.out.println(seasonEnum.getName());
seasonEnum.info();

但是实际项目开发过程中并没有这么简单,更常见的情况可能是,当是不同的枚举对象的时候需要执行不同的业务业务逻辑或者其他的方法,这样的话只有一个默认方法就不合理了。

既然可以添加统一的默认方法,也是可以为每一个枚举类通过重写的方式重写对应的方法或者独立的方法。

public enum SeasonEnum {
    SPRING("春"){
        @Override
        public void info() {
            System.out.println("春:重写info");
        }
    },
    SUMMER("夏"){
        @Override
        public void info() {
            System.out.println("夏:重写info");
        }
    },
    FALL("秋"){
        @Override
        public void info() {
            System.out.println("秋:重写info");
        }
    },
    WINTER("冬");

    SeasonEnum(String name){
        setName(name);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void info(){
        System.out.println("默认info方法");
    }

    public String name;
}

分别给SPRING、SUMMER、FALL重写了info方法,如果对应的枚举对象中已经重写了,就默认使用重写方法,若没有重写,则会调用默认的方法。

for(SeasonEnum SeasonEnum : SeasonEnum.values()){
    SeasonEnum.info();
}

枚举中也可以定义抽象方法,并且在对应的枚举对象中实现即可。

public enum Opt {
    PLUS {
        @Override
        public double eval(double a, double b) {
            return a + b;
        }
    },MINUS{
        @Override
        public double eval(double a, double b) {
            return a - b;
        }
    }, TIMES{
        @Override
        public double eval(double a, double b) {
            return a * b;
        }
    }, DEVIDE{
        @Override
        public double eval(double a, double b) {
            return a / b;
        }
    };

    public abstract double eval(double a, double b);
}

在定义的Opt枚举类中包含一个eval的抽象方法,并且在不同的枚举对象构造的时候实现了eval,因为对应的枚举对象是通过后面的大括号进行构造的,所以必须在对应的枚举类中进行实现否则就会报错。

for(Opt opt : Opt.values()){
    double result = opt.eval(20.0, 0.5);
    System.out.println(result);
}

既然可以在枚举类中定义抽象方法,那么其实也是可以在给枚举类使用接口的,我们还是上述的代码改造一下下。

for(SeasonEnum SeasonEnum : SeasonEnum.values()){
    SeasonEnum.info();
}

枚举中也可以定义抽象方法,并且在对应的枚举对象中实现即可。

public interface OptInFc {
    public double eval(double a, double b);
}

public enum Opt implements OptInFc {
    PLUS {
        @Override
        public double eval(double a, double b) {
            return a + b;
        }
    },MINUS{
        @Override
        public double eval(double a, double b) {
            return a - b;
        }
    }, TIMES{
        @Override
        public double eval(double a, double b) {
            return a * b;
        }
    }, DEVIDE{
        @Override
        public double eval(double a, double b) {
            return a / b;
        }
    };
}

同样是eval方法的实现,通过实现接口也同样是可以的,枚举类是没有办法继承抽象类,因为接口里的方法是不能有方法身体的,但抽象类的方法是可以有方法体的,继承后,在这一点上就会产生矛盾,抽象类的方法体无法存在了。

枚举在日常编码中几乎是必不可少的,如何用好,如何用精,还需要基础知识的铺垫,以上就是对于Enum类型的基础用法。