各位读者盆友,晚上好。这里分享下枚举的2种不常见的用法。
代码示例来源于 Thrid Edition
[美] Joshua Bloch 余黎敏译
一、最常用的用法
常见的多事枚举加个构造方法,然后根据所需用即可。eg:
package com.chm.effectivejava.myenum;
/**
* Author:meice Huang
* Time: 2020/3/8 下午8:22
*/
public enum Planet {
MERCURY(3.302e+23, 2.439e6),
VENUS(4.869e+24, 6.502e6),
EARTH(5.975e+24, 6.378e6),
MARS(6.419e+23, 6.378e6),
JUPITER(1.899e+27, 6.378e6),
SATURN(5.685e+26, 6.378e6),
URANUS(8.683e+25, 6.378e6),
NEPTUNE(1.204e+26, 6.378e6);
private final double mass;// In kilograms
private final double radius;// In meters
private final double surfaceGravity; //In m/s^2
private static final double G = 6.67E-11;
Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
this.surfaceGravity = G * mass / (radius * radius);
}
public double mass() {
return mass;
}
public double radius() {
return radius;
}
public double surfaceGravity() {
return surfaceGravity;
}
public double surfaceWeight(double mass) {
return mass * surfaceGravity; // F =ma;
}
}
二、特定于常量的方法实现
我们用2个版本的Option来阐述:
枚举中声明一个抽象方法,并在特定于常量的类主体中,用具体的方法覆盖每个常量的抽象方法——特定于常量的方法实现:constant-specific method implementation。具体总结见代码。
v1:
package com.chm.effectivejava.myenum;
public enum Operation {
PLUS, MINUS, TIMES, DIVIDE;
public 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("Unkonwn op:" + this);
}
/**
* 总结
* 1、增加枚举类型,需要同时在switch中增加,如果忘记会导致计算不准确;
* 2、:
*/
}
v2:
package com.chm.effectivejava.myenum;
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* 特定于常量的类主体constant-specific class body
* 特定于常量的方法的实现constant-specific method implementation
*/
public enum OperationV2 {
PLUS("+") {
@Override
public double apply(double x, double y) {
return x + y;
}
},
MINUS("-") {
@Override
public double apply(double x, double y) {
return x - y;
}
},
TIMES("*") {
@Override
public double apply(double x, double y) {
return x * y;
}
},
DIVIDE("/") {
@Override
public double apply(double x, double y) {
return x / y;
}
};
private final String symbol;
OperationV2(String symbol) {
this.symbol = symbol;
}
@Override
public String toString() {
return symbol;
}//重写了toString方法,意味着只要打印这个类,就是"+-*/"这种符号了,valueOf()方法就没法用了
public abstract double apply(double x, double y);
//注意:这里需要通过命令行运行哈:
public static void main(String[] args) {
double number1 = Double.parseDouble(args[0]);
double number2 = Double.parseDouble(args[1]);
for (OperationV2 op : values()) {
String result = String.format("%f %ps %f = %f", number1, op, number2, op.apply(number1, number2));
System.out.println(result);
}
}
private static final Map<String, OperationV2> stringToEnum = Arrays.stream(values())
.collect(Collectors.toMap(OperationV2::toString, e -> e));
public static Optional<OperationV2> fromString(String symbol) {
return Optional.ofNullable(stringToEnum.get(symbol));
}
/**
* 总结
* 1、特定于常量的方法的实现,这个在生产代码中少见;个人觉得会让枚举类显得很臃肿,不过确实是一种方案;
* 2、stringToEnum方法写得很好,用到了java8的知识;
* 3、fromString用到了Optional<>
*/
}
三、策略枚举Strategy enum
看过那本机械工业出版社的设计模式,里面把设计模式分为三类:创建型、结构型、行为型。Strategy就是策略模式。当时看了也是云里雾里,大体明白,不过还是不知道怎么用。看到这个策略枚举的时候,就清楚了。
这里也有2个版本。
v1:
package com.chm.effectivejava.myenum;
public enum PayrollDay {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY;
private static final int MINS_PER_SHIFT = 8 * 60;
int pay(int minutesWorked, int payRate) {
int basePay = minutesWorked * payRate;
int overtimePay;
switch (this) {
case SATURDAY:
case SUNDAY:
overtimePay = basePay / 2;
break;
default:
overtimePay = minutesWorked <= MINS_PER_SHIFT ? 0 : (minutesWorked - MINS_PER_SHIFT) * payRate / 2;
}
return basePay = overtimePay;
}
/**
* 总结
* 1、这段代码很简洁的表明了周内和周末算工资的方式;
* 2、不足:新增枚举的时候,需要改变switch。
* 类似这种枚举变化可能性还是很大的,但是频率会很低。最好能做到为每种类型的常量都有对应的报酬方式。
*/
}
v2:
package com.chm.effectivejava.myenum;
public enum PayrollDayV2 {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY(PayType.WEEKEND),
SUNDAY(PayType.WEEKEND);
private final PayType payType;
PayrollDayV2() {
this.payType = PayType.WEEKDAY;
}
PayrollDayV2(PayType payType) {
this.payType = payType;
}
int pay(int minsWorked, int payRate) {
// return PayType.pay(minsWorked,payRate);//这里虽然不能引用,但是大体就是这个思想
return 0;
}
private enum PayType {
WEEKDAY {
@Override
int overtimePay(int minsWorked, int payRate) {
return minsWorked <= MINS_PER_SHIFT ? 0 : (minsWorked - MINS_PER_SHIFT) * payRate / 2;
}
},
WEEKEND {
@Override
int overtimePay(int minsWorked, int payRate) {
return minsWorked * payRate / 2;
}
};
abstract int overtimePay(int mins, int payRate);
private static final int MINS_PER_SHIFT = 8 * 60;
int pay(int minsWorked, int payRate) {
int basePay = minsWorked * payRate;
return basePay + overtimePay(minsWorked, payRate);
}
}
/**
* 总结:
* 1、策略模式
* 2、如果扩展,非常方便且不会出错。加入新冠肺炎疫情期间,居家办公新增了一种考核方式
* VOVID-19-EXAMINE,
* 只需要新增一个PayType,重写overtimePay,然后在PayRollDayV2中新增一个常量并指定类型即可。
*
*/
}
枚举,并非日常所见那么简单。