Java枚举(enum)详解
Java 枚举
知识点
概念
enum的全称为 enumeration,是 JDK 1.5 中引⼊的新特性。
在Java中,被enum关键字修饰的类型就是枚举类型。形式如下:
enum Color { RED, GREEN, BLUE }
如果枚举不添加任何⽅法,枚举值默认为从0开始的有序数值。以 Color 枚举类型举例,它的枚举常量依次为RED:0,GREEN:1,BLUE:2。枚举的好处:可以将常量组织起来,统⼀进⾏管理。
枚举的典型应⽤场景:错误码、状态机等。
枚举类型的本质
尽管enum看起来像是⼀种新的数据类型,事实上,enum是⼀种受限制的类,并且具有⾃⼰的⽅法。
创建enum时,编译器会为你⽣成⼀个相关的类,这个类继承⾃java.lang.Enum。
java.lang.Enum类声明
public abstract class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable { ... }
枚举的⽅法
在enum中,提供了⼀些基本⽅法:
values():返回 enum 实例的数组,⽽且该数组中的元素严格保持在 enum 中声明时的顺序。
name():返回实例名。
ordinal():返回实例声明时的次序,从0开始。
getDeclaringClass():返回实例所属的 enum 类型。
equals():判断是否为同⼀个对象。
可以使⽤==来⽐较enum实例。
此外,java.lang.Enum实现了Comparable和Serializable接⼝,所以也提供compareTo()⽅法。
例:展⽰enum的基本⽅法
java switch case string
public class EnumMethodDemo {
enum Color {RED, GREEN, BLUE;}
enum Size {BIG, MIDDLE, SMALL;}
public static void main(String args[]) {
System.out.println("=========== Print all Color ===========");
for (Color c : Color.values()) {
System.out.println(c + " ordinal: " + c.ordinal());
}
System.out.println("=========== Print all Size ===========");
for (Size s : Size.values()) {
System.out.println(s + " ordinal: " + s.ordinal());
}
Color green = Color.GREEN;
System.out.println(<span class="hljs-string">"green name(): "</span> + green.name());
System.out.println(<span class="hljs-string">"green getDeclaringClass(): "</span> + DeclaringClass());
System.out.println(<span class="hljs-string">"green hashCode(): "</span> + green.hashCode());
System.out.println(<span class="hljs-string">"green compareTo Color.GREEN: "</span> + greenpareTo(Color.GREEN));
System.out.println(<span class="hljs-string">"green equals Color.GREEN: "</span> + green.equals(Color.GREEN));
System.out.println(<span class="hljs-string">"green equals Size.MIDDLE: "</span> + green.equals(Size.MIDDLE));
System.out.println(<span class="hljs-string">"green equals 1: "</span> + green.equals(<span class="hljs-number">1</span>));
System.out.format(<span class="hljs-string">"green == Color.BLUE: %b\n"</span>, green == Color.BLUE);
}
输出
=========== Print all Color ===========
RED ordinal: 0
GREEN ordinal: 1
BLUE ordinal: 2
=========== Print all Size ===========
BIG ordinal: 0
MIDDLE ordinal: 1
SMALL ordinal: 2
green name(): GREEN
green getDeclaringClass(): class org.umeration.EnumDemo$Color
green hashCode(): 460141958
green compareTo Color.GREEN: 0
green equals Color.GREEN: true
green equals Size.MIDDLE: false
green equals 1: false
green == Color.BLUE: false
枚举的特性
枚举的特性,归结起来就是⼀句话:
除了不能继承,基本上可以将enum看做⼀个常规的类。
但是这句话需要拆分去理解,让我们细细道来。
枚举可以添加⽅法
在概念章节提到了,枚举值默认为从0开始的有序数值。那么问题来了:如何为枚举显⽰的赋值。
Java 不允许使⽤ = 为枚举常量赋值
如果你接触过C/C++,你肯定会很⾃然的想到赋值符号=。在C/C++语⾔中的enum,可以⽤赋值符号=显⽰的为枚举常量赋值;但是,很遗憾,Java 语法中却不允许使⽤赋值符
号=为枚举常量赋值。
例:C/C++ 语⾔中的枚举声明
typedef enum{
ONE = 1,
TWO,
THREE = 3,
TEN = 10
} Number;
枚举可以添加普通⽅法、静态⽅法、抽象⽅法、构造⽅法
Java 虽然不能直接为实例赋值,但是它有更优秀的解决⽅案:为 enum 添加⽅法来间接实现显⽰赋值。
创建enum时,可以为其添加多种⽅法,甚⾄可以为其添加构造⽅法。
注意⼀个细节:如果要为enum定义⽅法,那么必须在enum的最后⼀个实例尾部添加⼀个分号。此外,在enum中,必须先定义实例,不能将字段或⽅法定义在实例前⾯。否则,
编译器会报错。
例:全⾯展⽰如何在枚举中定义普通⽅法、静态⽅法、抽象⽅法、构造⽅法
public enum ErrorCode {
OK(0) {
public String getDescription() {
return "成功";
}
},
ERROR_A(100) {
public String getDescription() {
return "错误A";
}
},
ERROR_B(200) {
public String getDescription() {
return "错误B";
}
};
<span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> code;
<span class="hljs-comment">// 构造⽅法:enum的构造⽅法只能被声明为private权限或不声明权限</span>
<span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-title">ErrorCode</span><span class="hljs-params">(<span class="hljs-keyword">int</span> number)</span> </span>{ <span class="hljs-comment">//    <span class="hljs-keyword">this</span>.code = number;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getCode</span><span class="hljs-params">()</span> </span>{ <span class="hljs-comment">// 普通⽅法    <span class="hljs-keyword">return</span> code;
} <span class="hljs-comment">// 普通⽅法</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> String <span class="hljs-title">getDescription</span><span class="hljs-params">()</span></span>; <span class="hljs-comm <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String args[])</spa    <span class="hljs-keyword">for</span> (ErrorCode s : ErrorCode.values()) {
System.out.println(<span class="hljs-string">"code: "</span> + s.getCode() + <span class="hljs-string">", description: "</span> + s.getDescription());
}
}
}
注:上⾯的例⼦并不可取,仅仅是为了展⽰枚举⽀持定义各种⽅法。下⾯是⼀个简化的例⼦
例:⼀个错误码枚举类型的定义
本例和上例的执⾏结果完全相同。
public enum ErrorCodeEn {
OK(0, "成功"),
ERROR_A(100, "错误A"),
ERROR_B(200, "错误B");
ErrorCodeEn(<span class="hljs-keyword">int</span> number, String description) {
<span class="hljs-keyword">this</span>.code = number;
<span class="hljs-keyword">this</span>.description = description;
}
<span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> code;
<span class="hljs-keyword">private</span> String description;
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getCode</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> code;
<span class="hljs-keyword">for</span> (ErrorCodeEn s : ErrorCodeEn.values()) {
System.out.println(<span class="hljs-string">"code: "</span> + s.getCode() + <span class="hljs-string">", description: "</span> + s.getDescription());
}
}
}
枚举可以实现接⼝
enum可以像⼀般类⼀样实现接⼝。
同样是实现上⼀节中的错误码枚举类,通过实现接⼝,可以约束它的⽅法。
public interface INumberEnum {
int getCode();
String getDescription();
}
public enum ErrorCodeEn2 implements INumberEnum {
OK(0, "成功"),
ERROR_A(100, "错误A"),
ERROR_B(200, "错误B");
ErrorCodeEn2(<span class="hljs-keyword">int</span> number, String description) {
<span class="hljs-keyword">this</span>.code = number;
<span class="hljs-keyword">this</span>.description = description;
}
<span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> code;
<span class="hljs-keyword">private</span> String description;
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getCode</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> code;
}
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getDescription</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> description;
}
}
枚举不可以继承
enum 不可以继承另外⼀个类,当然,也不能继承另⼀个 enum 。
因为enum实际上都继承⾃java.lang.Enum类,⽽ Java 不⽀持多重继承,所以enum不能再继承其他类,当然也不能继承另⼀个enum。
枚举的应⽤场景
组织常量
在JDK1.5 之前,在Java中定义常量都是public static final TYPE a;这样的形式。有了枚举,你可以将有关联关系的常量组织起来,使代码更加易读、安全,并且还可以使⽤枚举提供的⽅法。
枚举声明的格式
注:如果枚举中没有定义⽅法,也可以在最后⼀个实例后⾯加逗号、分号或什么都不加。
下⾯三种声明⽅式是等价的:
enum Color { RED, GREEN, BLUE }
enum Color { RED, GREEN, BLUE, }
enum Color { RED, GREEN, BLUE; }
switch 状态机
我们经常使⽤switch语句来写状态机。JDK7以后,switch已经⽀持int、char、String、enum类型的参数。这⼏种类型的参数⽐较起来,使⽤枚举的switch代码更具有可读性。enum Signal {RED, YELLO
W, GREEN}
public static String getTrafficInstruct(Signal signal) {
String instruct = "信号灯故障";
switch (signal) {
case RED:
instruct = "红灯停";
break;
case YELLOW:
instruct = "黄灯请注意";
break;
case GREEN:
instruct = "绿灯⾏";
break;
default:
break;
}
return instruct;
组织枚举
可以将类型相近的枚举通过接⼝或类组织起来。
但是⼀般⽤接⼝⽅式进⾏组织。
原因是:Java接⼝在编译时会⾃动为enum类型加上public static修饰符;Java类在编译时会⾃动为enum类型加上static修饰符。看出差异了吗?没错,就是说,在类中组织enum,
如果你不给它修饰为public,那么只能在本包中进⾏访问。
例:在接⼝中组织 enum
public interface Plant {
enum Vegetable implements INumberEnum {
POTATO(0, "⼟⾖"),
TOMATO(0, "西红柿");
Vegetable(<span class="hljs-keyword">int</span> number, String description) {
<span class="hljs-keyword">this</span>.code = number;
<span class="hljs-keyword">this</span>.description = description;
}
<span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> code;
<span class="hljs-keyword">private</span> String description;
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getCode</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getDescription</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
}
}
<span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">Fruit</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">INumberEnum</span> </span>{
APPLE(<span class="hljs-number">0</span>, <span class="hljs-string">"苹果"</span>),
ORANGE(<span class="hljs-number">0</span>, <span class="hljs-string">"桔⼦"</span>),
BANANA(<span class="hljs-number">0</span>, <span class="hljs-string">"⾹蕉"</span>);
Fruit(<span class="hljs-keyword">int</span> number, String description) {
<span class="hljs-keyword">this</span>.code = number;
<span class="hljs-keyword">this</span>.description = description;
}
<span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> code;
<span class="hljs-keyword">private</span> String description;
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getCode</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getDescription</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
}
}
}
例:在类中组织 enum
本例和上例效果相同。
public class Plant2 {
public enum Vegetable implements INumberEnum {...}  // 省略代码
public enum Fruit implements INumberEnum {...} // 省略代码
}
策略枚举
EffectiveJava中展⽰了⼀种策略枚举。这种枚举通过枚举嵌套枚举的⽅式,将枚举常量分类处理。
这种做法虽然没有switch语句简洁,但是更加安全、灵活。
例:EffectvieJava中的策略枚举范例
enum PayrollDay {
MONDAY(PayType.WEEKDAY), TUESDAY(PayType.WEEKDAY), WEDNESDAY(
PayType.WEEKDAY), THURSDAY(PayType.WEEKDAY), FRIDAY(PayType.WEEKDAY), SATURDAY(
PayType.WEEKEND), SUNDAY(PayType.WEEKEND);
<span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> PayType payType;
PayrollDay(PayType payType) {
<span class="hljs-keyword">this</span>.payType = payType;
}
<span class="hljs-function"><span class="hljs-keyword">double</span> <span class="hljs-title">pay</span><span class="hljs-params">(<span class="hljs-keyword">double</span> hoursWor
ked, <span class="hljs-keyword">double</span> p    <span class="hljs-keyword">return</span> payType.pay(hoursWorked, payRate);
}
<span class="hljs-comment">// 策略枚举</span>
<span class="hljs-keyword">private</span> <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">PayType</span> </span>{
WEEKDAY {
<span class="hljs-function"><span class="hljs-keyword">double</span> <span class="hljs-title">overtimePay</span><span class="hljs-params">(<span class="hljs-keyword">double</span> hours, <span class="hljs-keyword">double</s            <span class="hljs-keyword">return</span> hours <= HOURS_PER_SHIFT ? <span class="hljs-number">0</span> : (hours - HOURS_PER_SHIFT)
* payRate / <span class="hljs-number">2</span>;
}
},
WEEKEND {
<span class="hljs-function"><span class="hljs-keyword">double</span> <span class="hljs-title">overtimePay</span><span class="hljs-params">(<span class="hljs-keyword">double</span> hours, <span class="hljs-keyword">double</s            <span class="hljs-keyword">return</span> hours * payRate / <span class="hljs-number">2</span>;
}
};
<span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> HOURS_PER_SHIFT = <span class="hljs-number">8</span>;
<span class="hljs-function"><span class="hljs-keyword">abstract</span> <span class="hljs-keyword">double</span> <span class="hljs-title">overtimePay</span><span class="hljs-params">(<span class="hljs-keyword">double</span> hrs    <span class="hljs-function"><span class="hljs-key
word">double</span> <span class="hljs-title">pay</span><span class="hljs-params">(<span class="hljs-keyword">double</span> hoursWorked, <span class="hljs-keyword">double</span>        <span class="hljs-keyword">double</span> basePay = hoursWorked * payRate;
<span class="hljs-keyword">return</span> basePay + overtimePay(hoursWorked, payRate);
}
}
测试
System.out.println("时薪100的⼈在周五⼯作8⼩时的收⼊:" + PayrollDay.FRIDAY.pay(8.0, 100));
System.out.println("时薪100的⼈在周六⼯作8⼩时的收⼊:" + PayrollDay.SATURDAY.pay(8.0, 100));
EnumSet和EnumMap
Java 中提供了两个⽅便操作enum的⼯具类——EnumSet 和 EnumMap。
EnumSet是枚举类型的⾼性能Set实现。它要求放⼊它的枚举常量必须属于同⼀枚举类型。
EnumMap是专门为枚举类型量⾝定做的Map实现。虽然使⽤其它的 Map 实现(如HashMap)也能完成枚举类型实例到值得映射,但是使⽤ EnumMap 会更加⾼效:它只能接收同⼀枚举类型的实例作为键值,并且由于枚举类型实例的数量相对固定并且有限,所以 EnumMap 使⽤数组来存放与枚举类型对应的值。这使得 EnumMap 的效率⾮常⾼。
// EnumSet的使⽤
System.out.println("EnumSet展⽰");
EnumSet<ErrorCodeEn> errSet = EnumSet.allOf(ErrorCodeEn.class);
for (ErrorCodeEn e : errSet) {
System.out.println(e.name() + " : " + e.ordinal());
}
// EnumMap的使⽤
System.out.println("EnumMap展⽰");
EnumMap<StateMachine.Signal, String> errMap = new EnumMap(StateMachine.Signal.class);
errMap.put(StateMachine.Signal.RED, "红灯");
errMap.put(StateMachine.Signal.YELLOW, "黄灯");
errMap.put(StateMachine.Signal.GREEN, "绿灯");
for (Iterator<Map.Entry<StateMachine.Signal, String>> iter = Set().iterator(); iter.hasNext()  {
Map.Entry<StateMachine.Signal, String> entry = ();
System.out.Key().name() + " : " + Value());
}
作者:
欢迎任何形式的转载,但请务必注明出处。
限于本⼈⽔平,如果⽂章和代码有表述不当之处,还请不吝赐教。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。