天天看点

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

}