EffectiveJava(笔记五) 枚举和注解

来源:互联网 发布:nc数据库 编辑:程序博客网 时间:2024/06/06 03:14

30. 用enum代替int常量

枚举类型(enum type)是指由一组固定的常量组成合法值的类型, 在编程语言中还没有引入枚举类型之前, 表示枚举类型的床用模式是声明一组具有名的int常量

public static final int APPLE_FUJI = 0;public static final int APPLE_PIPPIN = 1;public static final int APPLE_GRANNY_SMITH = 2;

这种方法称作int枚举模式, 存在着诸多不足, 它在类型安全性和使用方便性没有任何帮助, 采用int枚举模式的程序是十分脆弱的, 因为int枚举是编译时常量, 被编译到使用它们的客户端中, 如果与枚举常量关联的int发生了变化, 客户端就必须重新编译

将int枚举常量翻译成可打印的字符串, 并没有很便利的方法, 如果将这种常量打印出来, 或者从调试器中将它显示出来, 就是一个数字, 并没有太大用处

如果用String枚举模式呢, 这种模式的常量提供了可打印的字符串, 但它会导致性能问题, 因为它依赖于字符串的比较操作, 而Java的枚举类型是功能十分齐全的类, 本质上是int值

包含同名常量的多个枚举类型可以在一个系统中和平共处, 因为每个类型都有自己的命名空间, 可以增加或者重新排列枚举类型中的常量, 而无需重新编译它的客户端代码, 因为常量值并没有被编译到客户端代码中, 而是在int枚举模式之中, 最终可以通过调用toString方法将枚举转换成可打印的字符串

处理完善int枚举模式的不足之处, 枚举类型还允许添加任意的方法和域, 并实现任意的接口, 它们提供了所有Object方法的高级实现, 实现了Comparable和Serializable接口, 并针对枚举类型的可任意改变性设计了序列化方式

31. 用实例域代替序数

枚举天生就与一个int值相关联, 所有的枚举都有一个ordinal方法, 它返回每个枚举常量在类型中的数字位置, 这虽然不错, 但是维护起来就像一场噩梦, 如果常量进行重新排序, 对应的常量int值也会发生变化, 所以解决这种问题的方法是: 永远不要根据枚举的序数导出与它关联的值, 而是要将它保存在一个实例域中, 如:

public enum Ensemble {    SOLO(1), DUET(2), TRIO(3);    private final int value    Ensemble(int value) {        this.value = value;         }    public int getValue() {        return this.value;    }}

Enum规范中的ordinal: 大多数程序员都不需要这个方法, 它是设计成用于像EnumSet和EnumMap这种基于枚举的通用数据结构的, 除非你在编写的是这种数据结构, 否则最好完全避免使用ordinal方法

32. EnumSet

public class Text {    public enum Style {BOLD, ITALIC, UNDERLINE, STRIKETHROUGH }    public void applyStyles(Set<Style> styles) { ... }}//EnumSet提供了丰富的静态工厂来轻松创建集合text.applyStyles(EnumSet.of(Style.BOLD, Style.ITALIC));

33. 用EnumMap代替序数索引

EnumMap是Map接口的实现, key-value中的key是Enum类型, 其原理就是一个对象数组, 数组的下标索引就是根据Map中的key直接获取, 即获取其中的ordinal值, 效率比HashMap高, 可以直接获取数组下标索引并访问到元素, 与数组不同的是, EnumMap支持泛型, 而数组不支持

public enum Operate {    ADD, UPDATE, DELETE;}//EnumMap初始化Map<Operate, String> map = new EnumMap<Operate, String>(Operate.class);//添加集合元素map.put(Operate.DELETE, "delete operate");//直接通过下标获取valuemap.get(Operate.DELETE);//直接通过下标移除元素map.remove(Operate.DELETE);//key遍历获取for(Operate operate : map.keySet()) {       }//value遍历获取for(String obj : map.values()) {}//key-value遍历获取for(Entry<Operate, String> entry : map.entrySet()) {}

34. 用接口模拟可伸缩的枚举

虽然枚举类型不是可扩展的, 但是接口类型则是可扩展的, 而enum类同样也可以实现接口
public interface Operation {
double apply(double x, double y);
}

public enum BasicOperation implements Operation {    PLUS("+") {        @Override        public double apply(double x, double y) {            return x + y;        }    };    private final String symbol;    BasicOperation(String symbol) {        this.symbol = symbol;    }
原创粉丝点击