一、什么场景下适合使用枚举?
枚举是一种特殊的数据类型,表面上它是一种类,但是它又比类多了很多特殊的用法,在JDK5引入枚举后,使得程序在表达几个固定值上显得更加简洁和安全。
我们都知道一个类可以实例化无数的对象实例,但是在有些场景下,一个类的实例个数是有限的,比如一周七天、一年四季、五大洋、七大洲等。这就比较适合使用枚举来定义它们。
二、枚举的表现形式是什么样的?
2.1 静态常量的使用方式
在以前的代码中,我们可能会使用下面这种常量定义的方式来表示一年四季:
public static final int SPRING = 1;
public static final int SUMMER = 2;
public static final int AUTUMN = 3;
public static final int WINTER = 4;
这种方式不是不可以,但是很明显它存在以下几种问题:
- 类型不安全
上面的四季本质上都是int类型的数据,在代码中如果出现SPRING + SUMMER
、AUTUMN * WINTER
这种用法,也不会存在什么问题,但这对于面向对象思想来说,是极其难以让人理解的,一年四季都是实例,怎么可以相互运算呢,这就称为类型不安全。 - 没有命名空间
如果当前类中还有其它的常量,那么就很容易发生混淆,程序员压根不会知道你想表达的是一个枚举实例。 - 输出内容无意义
如果在程序中打印出它们会输出1,2,3,4一样的魔法数字,除了你自己,无人知晓它们究竟代表什么。
2.2 简洁的枚举使用方式
因此,我们最好使用枚举来实现上述的这种需求场景:
public enum SeasonEnum{
SPRING,SUMMER,AUTUMN,WINTER;
}
当你需要某个枚举实例的时候,通过SeasonEnum.SPRING
来使用即可。一举解决了上述的三个问题。下面是一些补充内容:
- SeasonEnum.values()可以返回该枚举类内部所有的枚举实例;
- 在此种情况下打印枚举实例的结果等同于打印该枚举实例的
toString()
方法; - 使用
toString()
方法和使用name()
方法效果上是等效的,但是推荐使用前者; - 使用
ordinal()
方法将返回该实例在枚举类中声明的顺序索引值,从0开始算起;
2.3 推荐的枚举使用方式
通常情况下,我们会为每一个枚举实例指定具体的值,这些值具有更加确定性的意义,无论是在输出或者运算的时候都具有极大的便利性。
public enum SeasonEnum{
SPRING("春天"),SUMMER("夏天"),AUTUMN("秋天"),WINTER("冬天");
private String seasonDesc;
private SeasonEnum(String seasonDesc){
this.seasonDesc = seasonDesc;
}
public String getSeasonDesc(){
return this.seasonDesc;
}
}
在这样的使用方式中,我们有几个需要注意的点:
- 必须声明一个
private
私有的成员属性,这个属性用来存放实例的描述信息; - 必须同时创建一个私有的以上面声明的成员属性作为参数的构造函数,其作用是只有在该类内部才能创建实例对象,保护了枚举实例的个数不受外部代码的影响,同时也可以将实例的描述信息设置进去。
- 在枚举类的最开始逐个声明所有的需要的实例,相互之间以逗号分隔,最后以分号结尾,它们声明的方式就相当于调用上面的私有构造函数。
- 在外部代码中需要枚举实例的描述信息时,需要调用公共的
getSeasonDesc()
来获取。
三、我们该如何使用枚举?
3.1 通过枚举实例来获取该实例的信息
// 获取枚举实例的描述信息
System.out.println(SeasonEnum.SPRING.getSeasonDesc());
// 获取枚举实例的名字
System.out.println(SeasonEnum.SPRING.name());
// 获取枚举实例的名字
System.out.println(SeasonEnum.SPRING.toString());
// 获取枚举实例的索引值
System.out.println(SeasonEnum.SPRING.ordinal());
// 与同一枚举类下其它的枚举实例进行比较
System.out.println(SeasonEnum.SPRING.compareTo(SeasonEnum.SUMMER));
// 与同一枚举类下其它的枚举实例是否相等
System.out.println(SeasonEnum.SPRING.equals(SeasonEnum.SUMMER));
3.2 在switch中充当分支判断的条件
switch(SeasonEnum.SPRING){
case SPRING:
System.out.println("SPRING");
break;
case SUMMER:
System.out.println("SUMMER");
break;
default:
System.out.println("OTHERS");
}
3.3 实现接口中的抽象类让所有实例具有相同的行为
枚举类也是一种类结构,本质上当然可以实现接口、继承父类。倘若我们有一个接口:
public interface Year{
void printInfo();
}
该接口中有一个抽象方法printInfo()
需要在子类中实现,我们在实现的时候打印出一些信息:
public enum SeasonEnum implements Year{
SPRING("春天"),SUMMER("夏天"),AUTUMN("秋天"),WINTER("冬天");
private String seasonDesc;
private SeasonEnum(String seasonDesc){
this.seasonDesc = seasonDesc;
}
public String getSeasonDesc(){
return this.seasonDesc;
}
// 实现接口中的抽象方法
public void printInfo(){
System.out.println("SeasonEnum implements Year");
}
}
不管枚举类中哪一个实例,它们在调用printInfo()
的时候,输出的内容都是相同的,因此,此时它们就拥有相同的行为。
3.4 定义抽象类让不同的实例具有不同的行为
如果我们想让不同的实例对接口中的抽象方法表现出不同的行为,那么就需要在声明实例的时候自己去实现该抽象方法。
或者,如果我们在枚举类中自己定义一个新的抽象方法,那么也必须为所有的实例手动实现抽象的方法,其表现如下:
public enum SeasonEnum implements Year{
SPRING("春天"){
// 实现接口中的抽象方法
public void printInfo(){
// do something
}
// 实现枚举类中自己定义的抽象方法
public void printInfo2(){
// also do something
}
},SUMMER("夏天"){
// 实现接口中的抽象方法
public void printInfo(){
// do something
}
// 实现枚举类中自己定义的抽象方法
public void printInfo2(){
// also do something
}
},AUTUMN("秋天"){
// 实现接口中的抽象方法
public void printInfo(){
// do something
}
// 实现枚举类中自己定义的抽象方法
public void printInfo2(){
// also do something
}
},WINTER("冬天"){
// 实现接口中的抽象方法
public void printInfo(){
// do something
}
// 实现枚举类中自己定义的抽象方法
public void printInfo2(){
// also do something
}
};
private String seasonDesc;
private SeasonEnum(String seasonDesc){
this.seasonDesc = seasonDesc;
}
public String getSeasonDesc(){
return this.seasonDesc;
}
// 实现自己定义的抽象方法
public abstract void printInfo2()
}
全文完。