天天看點

Effective-Java讀書筆記【第六章上 枚舉】

第六章上 枚舉

第三十條 考慮用靜态工廠方法代替構造器

枚舉類型是繼承自抽象類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
*/
           

枚舉類圖:

Effective-Java讀書筆記【第六章上 枚舉】

第三十一條 用執行個體域代替序數

枚舉中有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這個抽象類,是以使用者開發的枚舉類型無法再擴充其他類,但是可以實作其他接口來提高伸縮性,以下是将第三十條的類圖增加接口:

Effective-Java讀書筆記【第六章上 枚舉】

實作接口示例:

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));
        }
    }

}