用 enum 代替 int 常量
Q:为什么要用 enum
代替 int
常量?
首先看一下 int
枚举模式的定义方式:
1 2 3 4 5 6 7 8 public static final int A_ONE = 1 ;public static final int A_TWO = 2 ;public static final int A_THREE = 3 ; public static final int B_ONE = 1 ;public static final int B_TWO = 2 ;public static final int B_THREE = 3 ;
这样的定义具有两个问题:
如果输出这些常量值,不会看出来有任何意义
很难获取到一共定义了多少这样的常量
接着看一下这样一个函数:
1 2 3 public void add (int x, int y) { System.out.println(x + y); }
这个函数期望传入两个都是
B
开头的常量值,但是此时传入任意
int
值编译并不会报错,且运行时也不会抛出异常,但是会造成一些莫名其妙的问题
还有一种 String
枚举模式,也许这种模式提供了可打印的常量值,但是它依赖于字符串的比较操作,这会有性能方面的问题。与此同时,如果项目中的其他成员不知道有这样的常量存在时,可能会用一些硬编码 的字符串,一旦这些字符串出现拼写错误,程序并不会检测出有任何问题,只有在程序运行过程才会一些不可预测的问题
这种情况下,就需要用到枚举类来代替这种模式了,通过反编译枚举的字节码文件来看一下枚举是怎么实现的,先看一下 Java 文件:
1 2 3 4 5 6 7 8 9 public enum IntEnum { ONE(1 ), TWO(2 ), THREE(3 ); IntEnum(int i) { } }
接下来是编译后的内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public final class IntEnum extends Enum { public static IntEnum[] values() { return (IntEnum[]) $VALUES.clone(); } public static IntEnum valueOf (String s) { return (IntEnum) Enum.valueOf(org / lovedev / effectivejava / _30 / IntEnum, s); } private IntEnum (String s, int i, int j) { super (s, i); } public static final IntEnum ONE; public static final IntEnum TWO; public static final IntEnum THREE; private static final IntEnum $VALUES[]; static { ONE = new IntEnum("ONE" , 0 , 1 ); TWO = new IntEnum("TWO" , 1 , 2 ); THREE = new IntEnum("THREE" , 2 , 3 ); $VALUES = (new IntEnum[]{ ONE, TWO, THREE }); } }
可以看出枚举是一个继承了
enum
抽象对象的
final
类,并且它的构造函数还是不可访问的,所以既不能实例化也不能扩展该类,只能访问类初始化时定义的几个
static final
实例对象
枚举还有还有一种将不同常量和行为关联起来方式,这种使用方式叫做特定于常量的方法实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public enum IntEnum { ONE(1 ) { @Override void test () { System.out.println("1" ); } }, TWO(2 ) { @Override void test () { System.out.println("2" ); } }, THREE(3 ) { @Override void test () { System.out.println("3" ); } }; IntEnum(int i) { } abstract void test () ; }
这种方式的优点在于,每当添加一个新的枚举类型时,就必须提供一个对应的方法实现,不会因为忘记添加实现导致程序出现异常
如果每个枚举常量都对应不同的行为,这种方式显然是没有任何问题的,但是在某些情况下只需要将这些枚举常量分为两种行为,比如工作日和休息日的起床时间,此时特定于常量的方法实现 就不能很好的共享代码,先看一下解决这个问题的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 enum WeekEnum { MONDAY(WAKEUPTIME.WEEKDAY), WEDNESDAY(WAKEUPTIME.WEEKDAY), TUESDAY(WAKEUPTIME.WEEKDAY), THURSDAY(WAKEUPTIME.WEEKDAY), FRIDAY(WAKEUPTIME.WEEKDAY), SATURDAY(WAKEUPTIME.WEEKEND), SUNDAY(WAKEUPTIME.WEEKEND); private WAKEUPTIME mWeekday; WeekEnum(WAKEUPTIME weekday) { mWeekday = weekday; } public int wakeUp () { return mWeekday.wakeUp(); } private enum WAKEUPTIME { WEEKDAY { @Override int wakeUp () { System.out.println("七点起床" ); return 7 ; } }, WEEKEND { @Override int wakeUp () { System.out.println("八点起床" ); return 8 ; } }; abstract int wakeUp () ; } }
把起床时间的定义放到一个嵌套的枚举类中,将这个
策略枚举 类传入到
WeekEnum
类中,把获取每天的起床时间交给
策略枚举 实现
枚举具有以下几个优势:
如果定义函数的参数是一个枚举类型,那么传入的参数必须是声明的枚举常量
可以实现接口,定义函数以及变量
由于枚举是通过实例化对象实现的,所以对比 Int 常量,性能上会有一点缺点,不过大多数情况下不用担心这个问题
评论