枚举的概念依旧停留在C、C++中的枚举。直到看到了设计模式中的使用枚举单例,发现自己对Java中的枚举真是一知半解。
本文参照:Java 枚举用法详解
1. 概述
枚举(enum)全写为的全称为:enumeration。是jdk1.5才新引进的概念,在Java中enum的有与C、C++相似的基本用法,也有很多扩展的用法。
尽管枚举类型看着像一种新的数据类型,但实际上它是一种受限制的类(继承自java.lang.Enum
)。
public enum ColorEnum{ RED,WHITE,BLUE,BLACK,GREEN}
经过编译器编译后产生的是一个class文件,该class文件经过反编译软件编译后可以看到实际上生成了一个类:
public class com.com.yarward.design.ColorEnum extends java.lang.Enum{ public static final com.com.yarward.design.ColorEnum RED; public static final com.com.yarward.design.ColorEnum WHITE; public static final com.com.yarward.design.ColorEnum BLUE; public static final com.com.yarward.design.ColorEnum BLACK; public static final com.com.yarward.design.ColorEnum GREEN; static {}; public static com.com.yarward.design.ColorEnum[] values(); public static com.com.yarward.design.ColorEnum valueOf(java.lang.String); ....}
/** * This is the common base class of all Java language enumeration types. * * <p> Note that when using an enumeration type as the type of a set * or as the type of the keys in a map, specialized and efficient * {@linkplain java.util.EnumSet set} and {@linkplain * java.util.EnumMap map} implementations are available. * * @since 1.5 */public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {...}
2. 特性介绍
2.1 基础用法
在未引入enum
之前,我们定义常量的时候总是使用这样的方式:public final static 类型....
,在引入了枚举类型之后,我们可以将一组相似属性统一组织、管理管理,并包含一些自己的方法:
name():返回枚举常量的名称。
ordinal():返回枚举常量的时序,0、1、2……。
getDeclaringClass():返回实例所属的enum类型。
equals(Object other):判断两个enum实例是否相等,也可用用==号来进行判断。
注意不能用直接用数字常量与枚举类型直接比较。
因为父类Enum实现了Comparable
和Serializable
的接口,因此也可以使用compareTo()
方法。
例:
ColorEnum colorEnumWhite = ColorEnum.WHITE;ColorEnum colorEnumRed = ColorEnum.RED;Log.d("test enum","enum colorEnumRed.toString() is " + colorEnumRed.toString());Log.d("test enum","enum colorEnumWhite is " + colorEnumWhite);Log.d("test enum","enum constant name is " + colorEnumWhite.name());Log.d("test enum","enum constant ordinal is " + colorEnumWhite.ordinal());Log.d("test enum","enum getDeclaringClass is " + colorEnumWhite.getDeclaringClass());Log.d("test enum","enum colorEnumWhite = colorEnumRed " + colorEnumWhite.equals(colorEnumRed));Log.d("test enum","enum colorEnumWhite = 1 " + colorEnumWhite.equals(1));Log.d("test enum","enum colorEnumWhite compareTo colorEnumRed " + colorEnumWhite.compareTo(colorEnumRed));
输出结果:
enum colorEnumRed.toString() is REDenum colorEnumWhite is WHITEconstant name is WHITEconstant ordinal is 1getDeclaringClass is class com.yarward.design.MainActivity$ColorEnumenum colorEnumWhite = colorEnumRed falseenum colorEnumWhite = 1 falseenum colorEnumWhite compareTo colorEnumRed 1
在使用enum时需要注意:
定义的枚举类型不可再继承其他的类型的类,因为它已经继承了java.lang.Enum
。
2.2 添加成员、方法
前面介绍过,枚举也是一种类,是一种受限制的类,不能在继承其他任何类的类。因此它也具有类应该有的特性。
java中的枚举与c/c++中的枚举不同,c/c++可以显示的为枚举类型赋值,而java中的枚举不可以。
/*C/C++可以显示的给枚举类型赋值*/typedef enum _Number{ one = 1, two, three, ten = 10} Number;
Java虽然不能直接为实例赋值,但是它有更优秀的解决方案:为 enum 添加方法来间接实现显示赋值。枚举类可以有自己的成员、构造方法、普通方法、抽象方法、静态方法。
唯一用起来比较别扭的一点是,enum相当于把类的实例化(枚举常量)放到了类声明的内部,这些枚举常量也可以重写枚举类型中的方法。
public enum WeekEnum{ Mon(true),Tue(true),Wen(true),Thu(true), Fri(true){ @Override public String helloEnum() { return "sad to work"; } }, Sat(false),Sun(false); private boolean mIsWork; private WeekEnum(boolean isWork){ this.mIsWork = isWork; } public boolean isWork(){ return mIsWork; } public String helloEnum(){ return "happy to work"; } public static String infoPrint(){ return "Monday to Sunday!"; } }
- 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
- 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
WeekEnum tue = WeekEnum.Tue;WeekEnum fri = WeekEnum.Fri;WeekEnum sat = WeekEnum.Sat;Log.d("test enum","enum static method :" + WeekEnum.infoPrint());Log.d("test enum","enum tue.helloEnum :" + tue.helloEnum());Log.d("test enum","enum fri.helloEnum :" + fri.helloEnum());Log.d("test enum","enum fri is work? " + fri.isWork());Log.d("test enum","enum sat is work? " + sat.isWork());
enum static method :Monday to Sunday! enum tue.helloEnum :happy to work enum fri.helloEnum :sad to work enum fri is work? true enum sat is work? false
2.3 实现接口
同样作为类,枚举类型也可以实现接口。用法和普通类的用法一致。
public interface IDoSomeThing{ public void doJobs();}
public enum WeekEnum implements IDoSomeThing{ Mon(true),Tue(true),Wen(true),Thu(true),Fri(true){ @Override public String helloEnum() { return "sad to work"; } }, Sat(false){ @Override public void doJobs() { Log.d("test enum","I am at resting"); } },Sun(false); ... ... @Override public void doJobs() { Log.d("test enum","I am at working"); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
fri.doJobs();sat.doJobs();
I am at workingI am at resting
3、枚举的典型应用场景
3 .1 常量
如果enum不添加任何方法的话,枚举的默认值为:从0开始的有序数值,每次递增1。
如果定义的枚举类型没有任何方法的话,可以在最后一个实例(常量)后加”,” 或”;” 或什么都不加。
注意:如果枚举类中添加了方法,则必须在最后一个实例后添加”;”
以下三种方式定义都可以:
enum ColorEnum{ RED, WHITE, BLUE, BLACK, GREEN }enum ColorEnum{ RED, WHITE, BLUE, BLACK, GREEN, }enum ColorEnum{ RED, WHITE, BLUE, BLACK, GREEN; }
3.2 switch中
enum Signal { GREEN, YELLOW, RED } public class TrafficLight { Signal color = Signal.RED; public void change() { switch (color) { case RED: color = Signal.GREEN; break; case YELLOW: color = Signal.RED; break; case GREEN: color = Signal.YELLOW; break; } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
3.3 组织枚举
可以将类型相近的枚举通过接口或类组织起来,但是一般用接口方式进行组织。
原因是:Java接口在编译时会自动为enum类型加上public static
修饰符;Java类在编译时会自动为 enum
类型加上static修饰符。看出差异了吗?
没错,就是说,在类中组织 enum
,如果你不给它修饰为 public
,那么只能在本包中进行访问。
在接口中不加public static修饰词,也能在其他包中被引用。
public interface INumberEnum{ public int getCode(); public String getDescription();}
public interface Plant { enum Vegetable implements INumberEnum { POTATO(0, "土豆"), TOMATO(0, "西红柿"); Vegetable(int number, String description) { this.code = number; this.description = description; } private int code; private String description; @Override public int getCode() { return 0; } @Override public String getDescription() { return null; } } enum Fruit implements INumberEnum { APPLE(0, "苹果"), ORANGE(0, "桔子"), BANANA(0, "香蕉"); Fruit(int number, String description) { this.code = number; this.description = description; } private int code; private String description; @Override public int getCode() { return 0; } @Override public String getDescription() { return null; } }}
- 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
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 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
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
3.4 策略枚举(没用过)
这种枚举通过枚举嵌套枚举的方式,将枚举实例(常量)分类处理,虽然没有通过switch语句使用简洁,但是更加灵活。
enum PayrollDay { MONDAY(PayType.WEEKDAY), TUESDAY(PayType.WEEKDAY), WEDNESDAY( PayType.WEEKDAY), THURSDAY(PayType.WEEKDAY), FRIDAY(PayType.WEEKDAY), SATURDAY( PayType.WEEKEND), SUNDAY(PayType.WEEKEND); private final PayType payType; PayrollDay(PayType payType) { this.payType = payType; } double pay(double hoursWorked, double payRate) { return payType.pay(hoursWorked, payRate); } private enum PayType { WEEKDAY { double overtimePay(double hours, double payRate) { return hours <= HOURS_PER_SHIFT ? 0 : (hours - HOURS_PER_SHIFT) * payRate / 2; } }, WEEKEND { double overtimePay(double hours, double payRate) { return hours * payRate / 2; } }; private static final int HOURS_PER_SHIFT = 8; abstract double overtimePay(double hrs, double payRate); double pay(double hoursWorked, double payRate) { double basePay = hoursWorked * payRate; return basePay + overtimePay(hoursWorked, payRate); } }}
- 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
- 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
Log.d("test enum","时薪100的人在周五工作8小时的收入:" + PayrollDay.FRIDAY.pay(8.0, 100));Log.d("test enum","时薪100的人在周六工作8小时的收入:" + PayrollDay.SATURDAY.pay(8.0, 100));
3.5 EnumSet和EnumMap
EnumSet
是枚举类型的高性能Set
实现。它要求放入它的枚举常量必须属于同一枚举类型。它的使用与其他的set没有什么不同,只是在构造的时候有一些特殊而已。
public void showEnumSetValue(EnumSet<ColorEnum> enumSet){ Log.d("enum test","enum set value is : "+ Arrays.toString(enumSet.toArray()));}
EnumSet<ColorEnum> enumEnumSet = EnumSet.noneOf(ColorEnum.class);showEnumSetValue(enumEnumSet);enumEnumSet.add(ColorEnum.BLACK);enumEnumSet.add(ColorEnum.BLUE);showEnumSetValue(enumEnumSet);EnumSet<ColorEnum> enumEnumSet1 = EnumSet.allOf(ColorEnum.class);showEnumSetValue(enumEnumSet1);EnumSet<ColorEnum> enumEnumSet2 = EnumSet.of(ColorEnum.GREEN,ColorEnum.WHITE);showEnumSetValue(enumEnumSet2);EnumSet<ColorEnum> enumEnumSet3 = EnumSet.range(ColorEnum.BLUE,ColorEnum.GREEN);showEnumSetValue(enumEnumSet3);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
enum set value is : []enum set value is : [BLUE, BLACK]enum set value is : [RED, WHITE, BLUE, BLACK, GREEN]enum set value is : [WHITE, GREEN]enum set value is : [BLUE, BLACK, GREEN]
EnumMap
是专门为枚举类型量身定做的Map
实现。虽然使用其它的Map实现(如HashMap)也能完成枚举类型实例到值得映射,但是使用EnumMap会更加高效:它只能接收同一枚举类型的实例作为键值,并且由于枚举类型实例的数量相对固定并且有限,所以EnumMap使用数组来存放与枚举类型对应的值。这使得EnumMap的效率非常高。
EnumMap<ColorEnum, String> enumStringEnumMap = new EnumMap<ColorEnum, String>(ColorEnum.class);enumStringEnumMap.put(ColorEnum.RED,"红色");enumStringEnumMap.put(ColorEnum.BLUE,"蓝色");enumStringEnumMap.put(ColorEnum.GREEN,"绿色");for(Map.Entry<ColorEnum,String> enumStringEntry : enumStringEnumMap.entrySet()){ Log.d("enum test","enum map key: " +enumStringEntry.getKey().name()+ " value " + enumStringEntry.getValue());}
enum map key: RED value 红色enum map key: BLUE value 蓝色enum map key: GREEN value 绿色