什么是枚举类
某些情况下,一个类的对象是有限的、固定的,比如一周有7天、一年有12个月等。这种情况下,就需要使用枚举类。JAVA中借助enum关键字构建枚举类,enum关键字和class、interface关键字地位相同,所以一个JAVA源文件中最多只能定义一个public修饰的枚举类,并且Java源文件必须和该枚举类类名相同。例如:
public enum seasonEnum {
SPRING,SUMMER,FALL,WINTER;
}
上述代码中定义了一个季节枚举类,在定义枚举类时,需要显示的列出所有枚举值,例如:SPRING,SUMMER,FALL,WINTER。枚举类的使用如下:
class enumTest{
public void judge(seasonEnum s) {
switch(s)
{
case SPRING:
System.out.println("春天");
break;
case SUMMER:
System.out.println("夏天");
break;
case FALL:
System.out.println("秋天");
break;
case WINTER:
System.out.println("冬天");
break;
}
}
}
public enum seasonEnum {
SPRING,SUMMER,FALL,WINTER;
public static void main(String[] args) {
for(seasonEnum s : seasonEnum.values()) {
System.out.println(s);
}
new enumTest().judge(seasonEnum.FALL);
}
}
上述代码中展示了两种不同的枚举类使用方法,一种借助枚举类默认的values()方法,直接返回枚举类的所有实例;另一种借助枚举类.实例(seasonEnum.FALL)来访问。同时可以看到,switch表达式中,可以使用枚举类对象。
总体来说,枚举类和普通类的区别有:
- 枚举类可以实现一个或多个接口,但不能显示的继承其他父类。因为JAVA中枚举类默认继承java.lang.Enum类,而并非object类
- 使用enum关键字定义非抽象枚举类时,默认使用final修饰,因此不能派生子类
- 枚举类的构造器只能使用private修饰
- 枚举类的实例必须在其代码块的第一行显示列出,否则该类永远无法产生实例。
- 枚举类的实例默认由public static final修饰
由于所有枚举类都继承了java.lang.Enum类,因此,所有枚举类都可以使用该类的方法,具体包括:
- int compareTo(E o):对比枚举对象的顺序
- String name():返回枚举实例的名称
- int ordinal():返回枚举对象在枚举类中的索引值(从0开始)
- String toString():返回枚举常量名称,与name方法类似,但toString方法更常用
- Enum.valueof(enumType,String name):返回指定枚举类中指定名称的枚举值
枚举类的成员变量、方法和构造器
枚举类中定义成员变量如下:
enum Gender{
MALE,FEMALE;
public String name;
}
public class GenderTest {
public static void main(String[] args) {
Gender.MALE.name = "女性";
Gender g = Enum.valueOf(Gender.class,"FEMALE");
g.name = "男性";
}
}
上述代码中,分别借助 Enum.valueof方法、枚举类.实例两个方法来访问枚举类实例。随后使用枚举类实例来访问成员变量。但是这种做法是很差的,因为JAVA应该把所有的类设计成良好封装的类,所以,不应该让类的实例直接访问类的成员变量,因为别的类可能对这个乱改,因此可以改进如下:
enum Gender{
MALE,FEMALE;
private String name;
public void setName(String name) {
switch(this) {
case MALE:
if (name.equals("男性")) {
this.name = name;
}
else
{
System.out.println("参数错误");
return;
}
break;
case FEMALE:
if(name.equals("女性")) {
this.name = name;
}
else
{
System.out.println("参数错误");
return;
}
break;
}
}
public String getName() {
return this.name;
}
}
public class GenderTest {
public static void main(String[] args) {
Gender.MALE.setName("男性");
//下方代码输出,参数错误
Gender.FEMALE.setName("男性");
System.out.println(Gender.MALE.getName());
}
}
上方代码保证了其他程序不能直接访问name,并且name的赋值必须合规。但是,实际上,这种处理方式依旧不够好,因为枚举类通常应该设计为不可变类,name值不应该被允许修改。
因此,枚举类的成员变量应该使用private final修饰。那么,就应该在构造器中为这些成员变量赋值,所以枚举类应该显示的定义含参构造器,进而,在列出枚举值时,就必须对应的传入参数。
综上,代码应改进为:
enum Gender{
MALE("男性"),FEMALE("女性");
private final String name;
private Gender(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
public class GenderTest {
public static void main(String[] args) {
System.out.println(Gender.FEMALE.getName());
}
}
这样成员变量就是不可变,只能访问的了。
在Gender枚举类创建了含参构造器之后,枚举类的第一行的实例可以直接调用,无需使用new关键字等。
枚举类实现接口
枚举类实现一个或多个接口和普通类基本相同,如下:
interface GenderDesc{
void info();
}
enum Gender implements GenderDesc{
MALE,FEMALE;
public void info() {
System.out.println("枚举类实现接口");
}
}
public class GenderTest {
public static void main(String[] args) {
Gender.FEMALE.info();
Gender.MALE.info();
}
}
两行代码均输出:枚举类实现接口。可见,不同实例的行为方式是相同的,如何实现不同实例的行为方式不同呢?如下:
interface GenderDesc{
void info();
}
enum Gender implements GenderDesc{
MALE
{
public void info() {
System.out.println("枚举类男性实例实现接口");
}
},
FEMALE
{
public void info() {
System.out.println("枚举类女性实例实现接口");
}
};
}
public class GenderTest {
public static void main(String[] args) {
Gender.FEMALE.info();
Gender.MALE.info();
}
}
当创建MALE、FEMALE两个枚举类实例时,后面紧跟了一对花括号,花括号中的部分其实就是一个类体部分,可以把这部分理解为匿名内部类。由此实现枚举类不同实例的行为方法不同。
包含抽象方法的枚举类
当枚举类包含抽象方法时,系统会自动为其添加abstract关键字,因此不可以人工再添加。由于枚举类需要显示的创建实例,而并非作为父类,因此定义每个枚举实例时,必须为抽象方法提供实现,否则报错,如下:
public enum Operation {
PLUS{
public double eval(double x, double y)
{
return x + y;
}
},
MINUS{
public double eval(double x,double y)
{
return x - y;
}
},
TIMES{
public double eval(double x,double y)
{
return x*y;
}
},
DIVIDE{
public double eval(double x,double y)
{
return x/y;
}
};
public abstract double eval (double x, double y);
public static void main(String[] args) {
System.out.println(Operation.PLUS.eval(3.1,5.5));
System.out.println(Operation.MINUS.eval(3.1,5.5));
System.out.println(Operation.TIMES.eval(3.1,5.5));
System.out.println(Operation.DIVIDE.eval(3.1,5.5));
}
}