枚举是什么?
枚举类型是指由一组固定的常量组成合法值的类型。枚举类型是解决常量int的另一种解决方案。
枚举类型的简单示例:
public enum AlarmPoints {STAIR1,STAIR2,LOBBY,OFFICE1,OFFICE2,OFFICE3,OFFICE4,BATHROOM,UTILITY,KITCHEN}
枚举的优势:
枚举类型是真正的final.所以客户端即不能创建,也不能对它进行扩展,因此很可能没有实例,而只有申明过的枚举常量。换句话说,枚举类型是实例受控的。它们是单例的泛型化。
枚举类型提供检查异常。
命名直观,可读性强。
实现了Comparable和Serialicable 接口。
简单枚举类型对于大多数枚举类型来说,已经够用了,但有的时候你可能需要更多的方法。有的时候,你需要将本质不同的行为和每个常量联系起来。比如,编写4中计算方式的枚举类型:
public enum Operation {
PLUS,
MIMUS,
TIMES,
DIVIDE;
double apply(double x,double y){
switch(this){
case PLUS: return x + y;
case MIMUS: return x - y;
case TIMES: return x * y;
case DIVIDE:return x / y;
}
throw new AssertionError("Unknown op:" + this);
}
}
这段代码看似可行,只是不太好看,如果没有throw语句,就没有办法编译通过,但是实际上不可能知道最后的语句。最糟糕的是,如果你添加了新的枚举类型,而switch中却没有添加,编译异常通过,运行却会出错。有一种解决方案:
public enum Operation {
PLUS{
@Override
double apply(double x, double y) {
return x + y;
}
},
MIMUS {
@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;
}
};
abstract double apply(double x,double y);
}
在这中写法下,你添加新常量,编译器会提醒你重写抽象方法,你也必须重写抽象方法;
它可以和特定的符号结合起来:
public enum Operation {
PLUS("+"){
@Override
double apply(double x, double y) {
return x + y;
}
},
MIMUS ("-"){
@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 sysmbol;
Operation(String sysmbol){
this.sysmbol = sysmbol;
}
@Override
public String toString() {
return sysmbol;
}
abstract double apply(double x, double y);
}
public static void main(String[] args) {
double x = 2.0;
double y = 4.0;
Arrays.asList(values()).forEach(o->{
System.out.printf("%f %s %f = %f%n",x,o,y,o.apply(x,y));
});
}
2.000000 + 4.000000 = 6.000000
2.000000 - 4.000000 = -2.000000
2.000000 * 4.000000 = 8.000000
2.000000 / 4.000000 = 0.500000
打印出了很工整的表单。
枚举类型有一个valueOf(String name)方法,可以将字符串转换成真是枚举类型,由于覆盖了toString 方法,所以编写一个fromString方法;
private static final Map<String,Operation> stringToEnum = new HashMap<>();
static {
Arrays.asList(Operation.values()).forEach(o-> stringToEnum.put(o.toString(),o));
}
public static Operation fromString(String symbol){
return stringToEnum.get(symbol);
}
特定于常量的方法实现中有一个美中不足的地方,它们使得枚举常量中共享代码变得更加困难了。比如,考虑用一个枚举表示薪资包中的工作天数。这个枚举中有一个方法,根据给定某工人的基本工资以及当天的工作时间,来计算它当天的报酬。在五个工作日中,超过8个小时算加班,加班有额外工资,周六,周天,全天算加班。
public enum PayrollDay {
MON,TUE,WED,THU,FRI,SAT,SUN;
private static final int HOUTS_PER_SHIFT = 8;
public double get(final int hours,double payRate){
double base = hours * payRate;
double over;
switch (this){
case SAT:case SUN:
over = hours * payRate * 0.5;
default: over = hours >= HOUTS_PER_SHIFT ?
(hours - HOUTS_PER_SHIFT) * payRate * 0.5: 0;
}
return base + over;
}
}
代码非常简洁,但是确难以维护,如果某人有一个工作日,确实放假,但是又忘记添加switch,程序依然可以运行,只是结果不正确而已。
你真正想要的就是每当添加一个枚举常量是,就强制选择一种加班报酬策略,又一种很好的方法可以实现这一点,添加一个枚举策略,来实现对加班工资的计算,虽然这种方式没有那么简洁,但却更加安全,也更加灵活:
public enum PayrollDay {
MON(PayType.WEEKDAY),TUE(PayType.WEEKDAY),WED(PayType.WEEKDAY),
THU(PayType.WEEKDAY),FRI(PayType.WEEKDAY),
SAT(PayType.WEEKEND),SUN(PayType.WEEKEND);
//支付方式
private final PayType payType;
//初始化payType
PayrollDay(PayType payType){
this.payType = payType;
}
//计算
public double pay(final int hours,double payRate){
return payType.get(hours,payRate);
}
enum PayType{
WEEKDAY {
@Override
double overGet(int hours, double payRate) {
return hours >= HOUTS_PER_SHIFT ?
(hours - HOUTS_PER_SHIFT) * payRate * 0.5: 0;
}
},
WEEKEND {
@Override
double overGet(int hours, double payRate) {
return hours * payRate * 0.5;
}
};
private static final int HOUTS_PER_SHIFT = 8;
abstract double overGet(final int hours,double payRate);
public double get(final int hours,double payRate){
double base = hours * payRate;
double over = overGet(hours,payRate);
return base + over;
}
}
}
测试:
public static void main(String[] args) {
double s = SUN.pay(10,10);
double s1 = MON.pay(10,10);
double s2 = MON.pay(7,10);
System.out.println(s + "..." + s1 + "..." + s2);
}
150.0...110.0...70.0
枚举中的switch语句适合于给外部的枚举类型增加特定于常量的行为,或改变,或替换
public class Demo{
public static Operation inverse(Operation o){
switch (o){
case PLUS: return MIMUS;
case MIMUS:return PLUS;
case TIMES:return DIVIDE;
case DIVIDE:return TIMES;
default: throw new AssertionError("Unknow o:" + o);
}
}
}
这里使用了静态导入