第六章上 枚舉
第三十條 考慮用靜态工廠方法代替構造器
枚舉類型是繼承自抽象類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 ;
}
};
}
}
通過泛型限制類型(第五章内容之後回顧):
public class OperationClass {
public static void main(String[] args) {
double x = Double.parseDouble(args[]);
double y = Double.parseDouble(args[]);
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));
}
}
}