第六章上 枚举
第三十条 考虑用静态工厂方法代替构造器
枚举类型是继承自抽象类Enum的抽象类,枚举值是继承自枚举类型的匿名实现类。
public enum Operation {
PLUS("+") { double apply(double x, double y) { return x + y;}},
MINUS("-") { double apply(double x, double y) { return x - y;}},
TIMES("*") { double apply(double x, double y) { return x * y;}},
DIVIDE("/") { double apply(double x, double y) { return x / y;}};
private final String symbol;
private static final Map<String, Operation> map = new HashMap<>();
static {
for (Operation op : values()) {
map.put(op.toString(), op);
}
}
public Operation formString() {
return map.get(this.symbol);
}
Operation(String symbol) {
this.symbol = symbol;
}
public String getSymbol() {
return symbol;
}
@Override
public String toString() {
return symbol;
}
abstract double apply(double x, double y);
}
class Test {
public static void main(String[] args) {
System.out.printf("枚举:%s%n", Operation.PLUS);
System.out.printf("枚举类:%s%n", Operation.PLUS.getClass());
System.out.printf("枚举父类:%s%n", Operation.PLUS.getClass().getSuperclass());
System.out.printf("枚举父类的父类:%s%n", Operation.PLUS.getClass().getSuperclass().getSuperclass());
System.out.printf("枚举父类的父类的父类:%s%n", Operation.PLUS.getClass().getSuperclass().getSuperclass().getSuperclass());
}
}
/*运行结果
枚举:+
枚举类:class com.xdt.effectiveJava.chapter6.part30.Operation$1
枚举父类:class com.xdt.effectiveJava.chapter6.part30.Operation
枚举父类的父类:class java.lang.Enum
枚举父类的父类的父类:class java.lang.Object
*/
枚举类图:
第三十一条 用实例域代替序数
枚举中有ordinal属性,但是最好不要用这个作为枚举的值来用(略)
第三十二条 用EnumSet代替位域
没有弄明白Set和位域有毛关系,反正就是说当要用到枚举集合时,就使用EnumSet。
以下是EnumSet的使用方式:
EnumSet.noneOf(E.class),
EnumSet.allOf(E.class),
EnumSet.of(E e1, E e2),
EnumSet.range(E from, E to)
使用示例:
public class Text {
public enum Style {BOLD, ITALIC, UNDERLINE, STRIKETHROUGH}
public void apply(Set<Style> styles) {
System.out.println(styles);
}
public static void main(String[] args) {
Set<Style> nonSet = EnumSet.noneOf(Style.class);
Set<Style> set = EnumSet.allOf(Style.class);
Set<Style> ofSet = EnumSet.of(Style.BOLD, Style.ITALIC);
Set<Style> ofSet1 = EnumSet.of(Style.BOLD, Style.UNDERLINE);
Set<Style> rangeSet = EnumSet.range(Style.BOLD, Style.UNDERLINE);
Set<Style> rangeSet1 = EnumSet.range(Style.BOLD, Style.STRIKETHROUGH);
System.out.printf("nonSet size=%s%n", nonSet.size()); //nonSet size=0
System.out.printf("%nallof set size=%s%n", set.size()); //allof set size=4
System.out.println("allof set:");
for (Style item : set) {
System.out.print(item + ","); //BOLD, ITALIC, UNDERLINE, STRIKETHROUGH
}
System.out.printf("%nofSet set size=%s%n", ofSet.size()); //ofSet set size=2
System.out.println("ofSet set:");
for (Style item : ofSet) {
System.out.print(item + ","); //BOLD, ITALIC,
}
System.out.printf("%nofSet1 set size=%s%n", ofSet1.size()); //ofSet1 set size=2
System.out.println("ofSet1 set:");
for (Style item : ofSet1) {
System.out.print(item + ","); //BOLD, UNDERLINE,
}
System.out.printf("%nrangeSet set size=%s%n", rangeSet.size());//rangeSet set size=3
System.out.println("rangeSet set:");
for (Style item : rangeSet) {
System.out.print(item + ","); //BOLD, ITALIC, UNDERLINE,
}
System.out.printf("%nrangeSet1 set size=%s%n", rangeSet1.size());//rangeSet1 set size=4
System.out.println("rangeSet1 set:");
for (Style item : rangeSet1) {
System.out.print(item + ","); //BOLD, ITALIC, UNDERLINE, STRIKETHROUGH
}
Set<Style> rangeSet2 = EnumSet.range(Style.STRIKETHROUGH, Style.ITALIC); //java.lang.IllegalArgumentException: STRIKETHROUGH > ITALIC
System.out.printf("rangeSet2 set size=", rangeSet2.size());//error
System.out.println("rangeSet2 set:");
for (Style item : rangeSet2) {
System.out.print(item + ",");
}
}
}
第三十三条 用EnumMap代替序数索引
EnumMap专门放Enum的键值对集合,使用时有较好的效率,以下是使用方式:
//枚举作为K,构造参数需要制定枚举的类型
Map<E, V> map = new EnumMap<>(E.class);
使用示例:
public class Herb {
public enum Type {ANNUAL, PERENNIAL, BIENNIAL}
private final String name;
private final Type type;
public Herb(String name, Type type) {
this.name = name;
this.type = type;
}
@Override
public String toString() {
return name;
}
public static void main(String[] args) {
Herb[] garden = {new Herb("rose", Type.ANNUAL), new Herb("Lily", Type.ANNUAL),
new Herb("Lilac", Type.PERENNIAL), new Herb("Clubs", Type.ANNUAL),};
//非最佳实例
/*Set<Herb>[] herbsByType = new Set[Type.values().length];
for (int i = 0; i < herbsByType.length; i++) {
herbsByType[i] = new HashSet();
}
for (Herb item : garden) {
herbsByType[item.type.ordinal()].add(item);
}*/
Map<Type, Set<Herb>> map = new EnumMap<>(Herb.Type.class);
for (Herb.Type t : Herb.Type.values()) {
map.put(t, new HashSet<>());
}
for (Herb item : garden) {
map.get(item.type).add(item);
}
System.out.println(map);
}
}
第三十四条 用接口模拟可伸缩的枚举
这一条其实是对第三十条的扩充,用户开发枚举类型,实际是继承了java.lang.Enum这个抽象类,所以用户开发的枚举类型无法再扩展其他类,但是可以实现其他接口来提高伸缩性,以下是将第三十条的类图增加接口:
实现接口示例:
public interface IOper {
double apply(double x, double y);
}
public enum Operation implements IOper {
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 String symbol;
Operation(String symbol){
this.symbol = symbol;
}
}
PS: 到此还是不太明白枚举底层如何实现,有一个疑问Operation实现IOper,但Operation并没有实现apply()方法,只有Operation的匿名内部类实现apply()方法,如果Operation是个正常的类不实现接口的方法是编译不通过的。
解决:通过反汇编来查看了Operation的字节码,先javac Operation.java产生class文件,然后javap -c Operation查看字节码,通过字节码知道Operation是abstract的,所以可以不实现接口的方法。(参考http://blog.csdn.net/zmx729618/article/details/66968590)
通过class模拟枚举:
public abstract class OperationClass implements IOper {
public static final OperationClass A;
static{
A = new OperationClass(){
public double apply(double x, double y) {
return 0;
}
};
}
}
通过泛型限制类型(第五章内容之后回顾):
public class OperationClass {
public static void main(String[] args) {
double x = Double.parseDouble(args[0]);
double y = Double.parseDouble(args[1]);
test(ExtendedOperation.class, x, y);
test(Arrays.asList(ExtendedOperation.values()), x, y);
}
private static <T extends Enum<T> & IOper> void test (Class<T> opSet, double x, double y) {
for (IOper op : opSet.getEnumConstants()) {
System.out.printf("%f %s %f = %f%n", x, op, y, op.apply(x, y));
}
}
private static void test(Collection<? extends IOper> opSet, double x, double y) {
for (IOper op : opSet) {
System.out.printf("%f %s %f = %f%n", x, op, y, op.apply(x, y));
}
}
}