Effective Java 学习笔记之枚举
1.用enum代替int常量
1)为了将数据和枚举常量关联起来 得声明实例域,并编写一个带有数据并将数据保存在域中的构造器。
2)枚举天生就是不可变的,因此所有的域都应该是final的。它们可以是公有的,但是最好做成私有并提供公有的访问方式
3)枚举类型还允许添加任意的方法和域 并实现任意的接口
4)提供了所有Object方法的高级实现 实现了Comparable和Serializable接口,并设计了序列化方式
public enum Planet {
MERCURY(3.302E+23, 2.439E6),
VENUS(4.869e+24, 6.052e6),
EARTH(5.975e+24, 6.378e6),
MARS(6.419e+23, 3.393e6),
JUPITER(1.899e+27, 7.149e7),
SATURN(5.685e+26, 6.027e7),
URANUS(8.683e+25, 2.556e7),
NEPTUNE(1.024e+26, 2.477e7);
private final double mass;
private final double radius;
private final double surfaceGravity;
private static final double G = 6.67300E-11;
Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
surfaceGravity = G * mass / (radius * radius);
}
public double getMass() {
return mass;
}
public double getRadius() {
return radius;
}
public double getSurfaceGravity() {
return surfaceGravity;
}
public double getSurfaceWeight(double mass) {
return mass * surfaceGravity;
}
}
扩展:需要为每个常量添加不同的行为
public enum Operation1 {
PLUS, MINUS, TIMES, DIVIDE;
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("Unknown op:" + this);
}
}
上面的代码存在一些问题,当为枚举类添加新的常量后,忘记为其提供相应apply的实现,编译不会出现错误提示,然而运行结果却不是希望的
因此可以进行修改
提供一个抽象的apply方法,这样每个常量都必须实现这个抽象方法,否则编译就会报错
PLUS {
@Override
double apply(double x, double y) {
return x + y;
}
},
MINUS {
@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);
有时候复写toString方法也会非常有用
public enum Operation2 {
PLUS("+") {
@Override
double apply(double x, double y) {
return x + y;
}
},
MINUS("-") {
@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);
private String symbol;
Operation2(String symbol) {
this.symbol = symbol;
}
@Override
public String toString() {
return symbol;
}
}
public class OpeartionTest {
public static void main(String[] args) {
double d1 = 1.3;
double d2 = 2.6;
for (Operation2 o:Operation2.values()){
System.out.printf("%f %s %f = %f%n",d1,o,d2,o.apply(d1,d2));
}
}
}
输出结果
1.300000 + 2.600000 = 3.900000
1.300000 - 2.600000 = -1.300000
1.300000 * 2.600000 = 3.380000
1.300000 / 2.600000 = 0.500000
5)枚举类型有一个自动产生的valueOf(String)方法,它将常量的名字转变成常量本身。
Operation2 plus = Operation2.valueOf("PLUS");
System.out.println(plus);
输出结果
+
如果在枚举类型中覆盖toString,要考虑编写一个fromString方法,将定制的字符串表示法变回相应的枚举,考虑用map保存
private static Map<String,Operation3> operation3Map = new HashMap<>();
static {
for (Operation3 o:Operation3.values()){
operation3Map.put(o.toString(),o);
}
}
public static Operation3 fromString(String str){
return operation3Map.get(str);
}
测试方法
Operation3 minus = Operation3.fromString("-");
System.out.println(minus);
输出结果
-
6)特定于常量的方法有一点不足,它们是的在枚举常量中共享代码变得困难
public enum PayrollDay {
MONDAY, TUESDAT, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
/** * 正常工作时间 */
private static final int HOURS_PER_SHIFT = 8;
/** * 计算工资 * * @param hoursWorked 工作时间 * @param payRate 薪资 * @return 工资 */
double pay(double hoursWorked, double payRate) {
double basePay = hoursWorked * payRate;
double overtimePay;
switch (this) {
case SATURDAY:
case SUNDAY:
overtimePay = hoursWorked * payRate / 2;
default:
overtimePay = hoursWorked <= HOURS_PER_SHIFT ? 0 : (hoursWorked - HOURS_PER_SHIFT) * payRate / 2;
}
return basePay + overtimePay;
}
}
当添加一个元素而没有添加相应的case时就会出现问题
可以使用枚举策略来解决,即讲加班工资的计算转移到一个私有的嵌套枚举中,将这个枚举的实例传到外部枚举的构造器中,这样外部枚举就可以将工资计算委托给内部枚举不需要再用switch进行转化了
public enum PayrollDay2 {
MONDAY(PayType.PAY_WEEKDAY),
TUESDAY(PayType.PAY_WEEKDAY),
WEDNESDAY(PayType.PAY_WEEKDAY),
THURSDAY(PayType.PAY_WEEKDAY),
FRIDAY(PayType.PAY_WEEKDAY),
SATURDAY(PayType.PAY_WEEKEND),
SUNDAY(PayType.PAY_WEEKEND);
private final PayType payType;
PayrollDay2(PayType payType) {
this.payType = payType;
}
public double pay(double hoursWork, double payRate) {
return this.payType.pay(hoursWork, payRate);
}
private enum PayType {
PAY_WEEKDAY {
@Override
double overtimePay(double hoursWork, double payRate) {
return hoursWork <= HOUR_SHIFT_TIME ? 0 : (hoursWork - HOUR_SHIFT_TIME) * payRate / 2;
}
},
PAY_WEEKEND {
@Override
double overtimePay(double hoursWork, double payRate) {
return hoursWork * payRate;
}
};
private static final int HOUR_SHIFT_TIME = 8;
abstract double overtimePay(double hoursWork, double payRate);
public double pay(double hoursWork, double payRate) {
double basePay = hoursWork * payRate;
double overtimePay = overtimePay(hoursWork, payRate);
return basePay + overtimePay;
}
}
}
2.用实例域代替序数
public enum Ensemble {
SOLO(1),
DUET(2),
TRIO(3),
QUARTET(4),
QUEINTET(5),
SEXTET(6),
SEPTET(7);
private final int numberOfMusicians;
Ensemble(int size) {
this.numberOfMusicians = size;
}
public int getNumberOfMusicians() {
return numberOfMusicians;
}
//public int numberOfMusicians() {
// //ordinal():返回每个枚举常量在类型中的数字位置
// return ordinal() + 1;
//}
}
3.用接口模拟可伸缩的枚举
public interface Operation {
double apply(double x, double y);
}
public enum BaseOperation implements Operation {
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;
}
};
}
public enum ExtendOperation implements Operation {
MOD {
@Override
public double apply(double x, double y) {
return x % y;
}
},
EXP {
@Override
public double apply(double x, double y) {
return Math.pow(x, y);
}
};
}
测试方法
public class ExtendOperationTest {
public static void main(String[] args) {
double d1 = 1.3;
double d2 = 2.6;
test(ExtendOperation.class, d1, d2);
test1(Arrays.asList(ExtendOperation.values()), d1, d2);
}
private static <T extends Enum<T> & Operation> void test(Class<T> opSet, double d1, double d2) {
for (Operation o : opSet.getEnumConstants()) {
System.out.printf("%f %s %f = %f%n", d1, o, d2, o.apply(d1, d2));
}
}
private static void test1(Collection<? extends Operation> opSet, double d1, double d2) {
for (Operation o : opSet) {
System.out.printf("%f %s %f = %f%n", d1, o, d2, o.apply(d1, d2));
}
}
}
输出结果
1.300000 MOD 2.600000 = 1.300000
1.300000 EXP 2.600000 = 1.978120
1.300000 MOD 2.600000 = 1.300000
1.300000 EXP 2.600000 = 1.978120
还没有评论,来说两句吧...