java 5引入了枚举,enum和class以及interface属于同级别的存在,我们在开发中使用类和接口比较多,使用枚举的情况比较少,下面介绍一下枚举。
枚举是将所有可能的情况一一列举出来,说明要描述的对象个数是有限的,例如:一年有12个月,一周有7天等。
1. java中枚举的定义
java中使用enum关键字定义枚举类,枚举类的实例对象要求一一罗列出来,对象间用“,”隔开,最后一个对象建议用“;”结束。可以有构造方法,必须是私有的,默认也是私有的,可以像其它类一样定义方法,成员变量,定义抽象方法的话枚举中定义的实例对象必须实现;通过enum定义的枚举类默认继承java.lang.Enum,且默认被final修饰,不能被继承,不过可以实现接口。
public enum SeasonEnum {
SPRING, SUMMER, AUTUMN, WINTER;
}
上面是枚举最简单的用法,定义枚举类建议类名以Enum结尾,枚举实例对象全部用大写字母,多单词组合可以用下划线连接单词。
2. 枚举类常用的方法
Enum类中有两个比较重要的成员变量name和ordinal
name和ordinal的值是在Enum的构造方法中赋值的,然而该构造方法是由编译器调用,不是由我们手动调用。
/**
* Sole constructor. Programmers cannot invoke this constructor.
* It is for use by code emitted by the compiler in response to
* enum type declarations.
*
* @param name - The name of this enum constant, which is the identifier
* used to declare it.
* @param ordinal - The ordinal of this enumeration constant (its position
* in the enum declaration, where the initial constant is assigned
* an ordinal of zero).
*/
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
那么对于上面SeasonEnum定义的枚举,枚举实例的name和ordinal分别是什么呢?
public enum SeasonEnum {
SPRING, SUMMER, AUTUMN, WINTER;
}
//对于春夏秋冬实例编译器通过下面的构造方法怎么创建呢?
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
//name是实例的字符串名称,ordinal是从0开始依次+1
SPRING = new Enum("SPRING", 0);
SUMMER = new Enum("SUMMER", 1);
AUTUMN = new Enum("AUTUMN", 2);
WINTER = new Enum("WINTER", 3);
name()和ordinal()方法分别返回是name和ordinal的值,Enum的toString方法默认也是返回的name属性。compareTo方法比较的是两个枚举实例的ordinal差值。
还有一个编译器为我们生成的静态values()方法,返回我们定义的枚举实例对象的数组
SeasonEnum[] seasonEnums = SeasonEnum.values();
for (SeasonEnum seasonEnum : seasonEnums) {
Log.e(TAG, "main: seasonEnum=" + seasonEnum);
}
//运行输出结果就是
SPRING
SUMMER
AUTUMN
WINTER
3. Android系统源码中枚举的使用
//时钟样式枚举,数字和表盘两种样式
//源码链接如下http://androidxref.com/9.0.0_r3/xref/packages/apps/DeskClock/src/com/android/deskclock/data/DataModel.java#55
/** Indicates the display style of clocks. */
public enum ClockStyle {ANALOG, DIGITAL}
/**
* The preferred starting day of the week can differ by locale. This enumerated value is used to
* describe the preferred ordering.
*/
public enum Order {
SAT_TO_FRI(SATURDAY, SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY),
SUN_TO_SAT(SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY),
MON_TO_SUN(MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY);
private final List<Integer> mCalendarDays;
Order(Integer... calendarDays) {
mCalendarDays = Arrays.asList(calendarDays);
}
public List<Integer> getCalendarDays() {
return mCalendarDays;
}
}
/**
* 源码ImageView的缩放类型,android的老铁们肯定不陌生
*/
public enum ScaleType {
MATRIX (0),
FIT_XY (1),
FIT_START (2),
FIT_CENTER (3),
FIT_END (4),
CENTER (5),
CENTER_CROP (6),
CENTER_INSIDE (7);
ScaleType(int ni) {
nativeInt = ni;
}
final int nativeInt;
}
4. 模拟演练
上图是redmi k40相机录像模式中的功能,针对视频的尺寸和帧率这里用枚举实现一下(特别声明:本人不是小米红米员工,也没看过他们的代码,只是根据自己的理解来用枚举实现,如果有做过相机类似功能的大佬也欢迎提出正宗的方案)
public enum VideoSizeFPSEnum {
VIDEO_720P_30FPS(1280, 720, 30),
VIDEO_1080P_30FPS(1920, 1080, 30),
VIDEO_1080P_60FPS(1920, 1080, 60),
VIDEO_4K_30FPS(3840, 2160, 30);
private final int videoWidth;
private final int videoHeight;
private final int videoFPS;
VideoSizeFPSEnum(int videoWidth, int videoHeight, int videoFPS) {
this.videoWidth = videoWidth;
this.videoHeight = videoHeight;
this.videoFPS = videoFPS;
}
public int getVideoWidth() {
return videoWidth;
}
public int getVideoHeight() {
return videoHeight;
}
public int getVideoFPS() {
return videoFPS;
}
}
//根据业务逻辑用枚举可能会写类似这样的代码
private void switchVideoSizeFPS(VideoSizeFPSEnum videoSizeFPSEnum) {
switch (videoSizeFPSEnum) {
case VIDEO_720P_30FPS:
//保存状态建议保存name值,videoSizeFPSEnum.name();
//省略切换尺寸相关逻辑...
break;
case VIDEO_1080P_30FPS:
break;
case VIDEO_1080P_60FPS:
break;
case VIDEO_4K_30FPS:
break;
}
}
private VideoSizeFPSEnum getUserVideoSizeFPS() {
//假设我们拿到了上次保存的name值为 videoSizeFPSName
String videoSizeFPSName = "上次保存的name值";
//有了name值我们可以直接得到对应的枚举对象,这也是为什么推荐保存name的原因
//不过也要考虑枚举实例变动或者是保存的name值被串改的情况,name不匹配会抛出异常
VideoSizeFPSEnum videoSizeFPSEnum = VideoSizeFPSEnum.valueOf(videoSizeFPSName);
return videoSizeFPSEnum;
}