前言
java枚举是在开发过程中用的最多的类,这里对java之前的枚举常量类和枚举做了一个分析,并且对枚举相关知识拾遗。
枚举类
在出现枚举之前,通常是一个final类去表示”可枚举”这个概念,比如下面这个列举数字的枚举类
1 | /** |
可以看到枚举类的特点:
- 成员用常量来表示,并且类型为当前类型(当前类型)
- 常被设置为final类
- 非public构造器(自己内部来创建实例)
这样有些缺点,比如:
- 枚举的输出打印的时候要怎么做?每个成员是第几个定义的?要想达到这些操作就必须要写一些方法,而每个枚举类去这样写这样的方法是比较蛋疼的,因为他不具有枚举的values方法。
枚举
这里写出对应的java枚举
1 | enum CountingEnum { |
这里如果想要输出对应的名字和顺序,那么就十分方便了。
1 | // 输出 枚举 中的名字、位置、输出所有枚举 |
可以看到输出:
这是因为java所有的枚举都是继承Enum抽象类的,而valueOf()方法、ordinal()方法、name()方法都是定义在其中的,可以看下Eunm抽象类。
1 |
|
仔细看也许你会有两个疑问:
- 没看到显示的定义CountingEnum时继承Enum类的?
- values方法也没有看到在父类中定义?
对这两个疑问我们可以去看这个类对应的字节码:
可以看到:
- enum其实也是final class, 虽然没有显示继承,但是其实是继承了
Enum<T>
类的,所以可以访问到对应的name,ordinal字段,这个设计让我们输出枚举一些信息的时候很便捷。也提供了valueOf方法,也可以在动态判断枚举的时候使用。
在来看下边的字节码:
可以看到是有values方法,其实这个是jvm通过字节码提升的方式去为枚举做的优化。所以使用枚举可以快速遍历并且一些输出之类的操作。
可以总结下枚举的特点:
- 枚举其实就是final class,并且继承java.lang.Enum抽象类。
- 枚举可以实现接口。
- 枚举不能显示的继承和被继承
- 留个坑:既然是final class,那么枚举里可以定义抽象方法吗?
枚举中抽象方法的设计
我们在看基础语法的时候,总是说final 和 abstract是互斥的,所以想当然的认为枚举中不能定义抽象方法,但结论其实是可以的。
我们先看一个枚举来实现加减操作的例子:
1 | enum Opration { |
这个实现其实是通过在枚举中加入了非枚举含义的方法和域来实现的操作的一个类型枚举。但是有个问题,当拓展新的操作符时,需要破坏switch中的逻辑,这个不太符合开闭原则,这时候就可以通过把apply作为抽象方法,使得拓展时只需要实现符合自己的抽象逻辑。
1 | /** |
所以枚举是可以定义抽象方法的。
jdk中其实也有对应的例子,可以看下TimeUnit这个时间单位枚举,枚举类型都是通过实现抽象方法(其实是返回异常的普通方法,思想是一样的)来实现不同时间单位的转化。
彩蛋
如何给上边的枚举类实现一个values方法?
因为需要遍历所有的字段,所以很自然的想到了反射去实现。这里需要注意,因为枚举类定义的枚举都是public static final,而作为val变量是int的一个修饰符,需要将除了枚举外的val变量排除~
示例代码:
1 | final class EnumClass { |