概述
枚举
- 前言
- 基本 enum 特性
- 方法添加
- 覆盖 enum 的方法
- switch 语句中的 enum
- values 方法的神秘之处
- EnumSet
- EnumMap
- 常量特定方法
- 本章小结
前言
关键字 enum 可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用。这是一种非常有用的功能
基本 enum 特性
调用 enum 的 values()
方法,可以遍历 enum 实例 .values()
方法返回 enum 实例的数组,而且该数组中的元素严格保持其在 enum 中声明时的顺序,因此你可以在循环中使用 values()
返回的数组。
public enum EnumClass {
ONE,TWO,THREE
}
public class Test {
public static void main(String[] args) {
for (EnumClass enumClass:EnumClass.values()) {
System.out.println(enumClass);
}
/** Output:
* ONE
* TWO
* THREE
*/
}
方法添加
除了不能继承自一个 enum 之外,我们基本上可以将 enum 看作一个常规的类。也就是说我们可以向 enum 中添加方法。enum 甚至可以有 main()
方法。
public enum EnumClass {
ONE("one","一"),
TWO("two","二"),
THREE("three","三");
private String code;
private String value;
EnumClass(String code, String value) {
this.code = code;
this.value = value;
}
public static String getValues(String code){
for (EnumClass enumClass:EnumClass.values()) {
if(enumClass.code.equals(code)){
return enumClass.value;
}
}
return null;
}
public static void main(String[] args) {
for (EnumClass enumClass:EnumClass.values()) {
System.out.println(enumClass.code+","+enumClass.value);
}
/** Output:
* one,一
* two,二
* three,三
*/
}
}
注意,如果你打算定义自己的方法,那么必须在 enum 实例序列的最后添加一个分号。同时,Java 要求你必须先定义 enum 实例。如果在定义 enum 实例之前定义了任何方法或属性,那么在编译时就会得到错误信息。
enum 中的构造器与方法和普通的类没有区别,因为除了有少许限制之外,enum 就是一个普通的类。所以,我们可以使用 enum 做许多事情(虽然,我们一般只使用普通的枚举类型)
覆盖 enum 的方法
覆盖 toSring()
方法,给我们提供了另一种方式来为枚举实例生成不同的字符串描述信息。 在下面的示例中,我们使用的就是实例的名字,不过我们希望改变其格式。覆盖 enum 的 toSring()
方法与覆盖一般类的方法没有区别:
public enum EnumClass {
ONE,
TWO,
THREE;
@Override
public String toString() {
String name = name();
return name.charAt(0)+name.substring(1).toLowerCase();
}
public static void main(String[] args) {
for (EnumClass enumClass:EnumClass.values()) {
System.out.println(enumClass);
}
/** Output:
* One
* Two
* Three
*/
}
}
switch 语句中的 enum
在 switch 中使用 enum,是 enum 提供的一项非常便利的功能。一般来说,在 switch 中只能使用整数值,而枚举实例天生就具备整数值的次序,并且可以通过 ordinal()
方法取得其次序(显然编译器帮我们做了类似的工作),因此我们可以在 switch 语句中使用 enum。
虽然一般情况下我们必须使用 enum 类型来修饰一个 enum 实例,但是在 case 语句中却不必如此。下面的例子使用 enum 构造了一个小型状态机:
public enum EnumClass {
ONE,
TWO,
THREE;
public static void main(String[] args) {
EnumClass enumClass = EnumClass.ONE;
for (int i = 0; i < 3; i++) {
switch (enumClass){
case ONE:
enumClass = EnumClass.TWO;
System.out.println("one");
break;
case TWO:
enumClass = EnumClass.THREE;
System.out.println("two");
case THREE:
enumClass = EnumClass.ONE;
System.out.println("three");
break;
}
}
/** Output:
* one
* two
* three
* one
*/
}
}
values 方法的神秘之处
我们可以利用反射机制编写一个简单的程序,来查看其中的究竟:
public enum EnumClass {
ONE,
TWO,
THREE;
public static void main(String[] args) {
Class cla = EnumClass.class;
System.out.println(cla.getSuperclass());
Set<String> stringSet = new HashSet<>();
for (Method method:cla.getMethods()) {
stringSet.add(method.getName());
}
System.out.println(stringSet);
/** Output:
* class java.lang.Enum
* [getClass, wait, valueOf, values, notifyAll, main, compareTo, notify, getDeclaringClass, hashCode, equals, name, toString, ordinal]
*/
}
}
我们已经知道,所有的 enum 都继承自 Java.lang.Enum
类。由于 Java 不支持多重继承,所以你的 enum 不能再继承其他类。反编译后:
values()
是由编译器添加的 static 方法。由于 values()
方法是由编译器插入到 enum 定义中的 static 方法,所以,如果你将 enum 实例向上转型为 Enum,那么 values()
方法就不可访问了。
EnumSet
Set 是一种集合,只能向其中添加不重复的对象。Java SE5 引入 EnumSet,是为了通过 enum 创建一种替代品,以替代传统的基于 int 的“位标志”。这种标志可以用来表示某种“开/关”信息,不过,使用这种标志,我们最终操作的只是一些 bit,而不是这些 bit 想要表达的概念,因此很容易写出令人难以理解的代码。
EnumSet 的设计充分考虑到了速度因素,因为它必须与非常高效的 bit 标志相竞争(其操作与 HashSet 相比,非常地快),就其内部而言,它(可能)就是将一个 long 值作为比特向量,所以 EnumSet 非常快速高效。使用 EnumSet 的优点是,它在说明一个二进制位是否存在时,具有更好的表达能力,并且无需担心性能。
public enum EnumClass {
ONE,
TWO,
THREE;
public static void main(String[] args) {
EnumSet enumSet = EnumSet.allOf(EnumClass.class);
enumSet.add(EnumClass.ONE);
System.out.println(enumSet);
enumSet.remove(ONE);
System.out.println(enumSet);
/** Output:
* [ONE, TWO, THREE]
* [TWO, THREE]
*/
}
}
使用 static import 可以简化 enum 常量的使用。EnumSet 的方法的名字都相当直观,你可以查阅 JDK 文档找到其完整详细的描述。如果仔细研究了 EnumSet 的文档,你还会发现 of()
方法被重载了很多次,不但为可变数量参数进行了重载,而且为接收 2 至 5 个显式的参数的情况都进行了重载。这也从侧面表现了 EnumSet 对性能的关注。因为,其实只使用单独的 of()
方法解决可变参数已经可以解决整个问题了,但是对比显式的参数,会有一点性能损失。采用现在这种设计,当你只使用 2 到 5 个参数调用 of()
方法时,你可以调用对应的重载过的方法(速度稍快一点),而当你使用一个参数或多过 5 个参数时,你调用的将是使用可变参数的 of()
方法。注意,如果你只使用一个参数,编译器并不会构造可变参数的数组,所以与调用只有一个参数的方法相比,也就不会有额外的性能损耗。
EnumMap
EnumMap 是一种特殊的 Map,它要求其中的键(key)必须来自一个 enum,由于 enum 本身的限制,所以 EnumMap 在内部可由数组实现。因此 EnumMap 的速度很快,我们可以放心地使用 enum 实例在 EnumMap 中进行查找操作。不过,我们只能将 enum 的实例作为键来调用 put()
可方法,其他操作与使用一般的 Map 差不多。
public enum EnumClass {
ONE,
TWO,
THREE;
public static void main(String[] args) {
EnumMap enumMap = new EnumMap(EnumClass.class);
System.out.println(enumMap);
enumMap.put(EnumClass.ONE,"one");
enumMap.put(EnumClass.TWO,"two");
enumMap.put(EnumClass.THREE,"three");
System.out.println(enumMap);
enumMap.put(EnumClass.THREE,"3");
System.out.println(enumMap);
/** Output:
* {}
* {ONE=one, TWO=two, THREE=three}
* {ONE=one, TWO=two, THREE=3}
*/
}
}
与 EnumSet 一样,enum 实例定义时的次序决定了其在 EnumMap 中的顺序。相比,EnumMap 有一个优点,那 EnumMap 允许程序员改变值对象,而常量相关的方法在编译期就被固定了。
常量特定方法
Java 的 enum 有一个非常有趣的特性,即它允许程序员为 enum 实例编写方法,从而为每个 enum 实例赋予各自不同的行为。要实现常量相关的方法,你需要为 enum 定义一个或多个 abstract 方法,然后为每个 enum 实例实现该抽象方法。
public enum EnumClass {
ONE{
@Override
void getInfo() {
System.out.println("one");
}
},
TWO{
@Override
void getInfo() {
System.out.println("two");
}
},
THREE{
@Override
void getInfo() {
System.out.println("three");
}
};
abstract void getInfo();
public static void main(String[] args) {
for (EnumClass enumClass:values()){
enumClass.getInfo();
}
/** Output:
* one
* two
* three
*/
}
}
在面向对象的程序设计中,不同的行为与不同的类关联。而通过常量相关的方法,每个 enum 实例可以具备自己独特的行为,这似乎说明每个 enum 实例就像一个独特的类。在上面的例子中,enum 实例似乎被当作其“超类”ConstantSpecificMethod 来使用,在调用 getInfo()
方法时,体现出多态的行为。
除了实现 abstract 方法以外,程序员是否可以覆盖常量相关的方法呢?答案是肯定的,参考下面的程序:
public enum EnumClass {
ONE {
@Override
void getInfo() {
System.out.println("one");
}
},
TWO {
@Override
void getInfo() {
System.out.println("two");
}
},
THREE {
@Override
void getInfo() {
System.out.println("three");
}
};
void getInfo() {
System.out.println("getInfo");
}
public static void main(String[] args) {
for (EnumClass enumClass : values()) {
enumClass.getInfo();
}
/** Output:
* one
* two
* three
*/
}
}
本章小结
虽然 Java 中的枚举比 C 或 **C++**中的 enum 更成熟,但它仍然是一个“小”功能,Java 没有它也已经(虽然有点笨拙)存在很多年了。有时恰恰因为它,你才能够优雅而干净地解决问题。优雅与清晰很重要,正是它们区别了成功的解决方案与失败的解决方案。而失败的解决方案就是因为其他人无法理解它。
虽然枚举类型本身并不是特别复杂,程序员可以将 enum 与 Java 语言的其他功能结合使用,例如多态、泛型和反射。
最后
以上就是俏皮蜜蜂为你收集整理的重拾Java基础知识:枚举前言的全部内容,希望文章能够帮你解决重拾Java基础知识:枚举前言所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复