前言
枚举是 JDK 1.5 引入的类型,它使得我们在需要群组并使用枚举类型集时,可以很方便地处理。
基本使用
用法一:常量。
public class EnumTest {
/* static final int MONDAY = 0;
static final int TUESDAY = 1;
static final int WEDNESDAY = 2;
static final int THURSDAY = 3;
static final int FRIDAY = 4;
static final int SATURDAY = 5;
static final int SUNDAY = 6;*/
enum Days{
MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY,SUNDAY
}
public static void main(String[] args) {
for (Days days : Days.values()) {
System.out.println(days.name() + " " + days.ordinal());
}
}
}
其实这种用法完全可以用常量来替换的嘛,但是仔细对比,如果常量多的时候,是不是用枚举更优雅,代码量更少一点呢?
用法二:switch。我们知道,在 JDK 1.7 之前 switch 是不支持 String 类型的,但是如果我们有这个需求怎么解决呢?答案就是我们可以通过枚举来实现。
public class SwitchStringByEnum {
enum Days {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
public static void printDay(String day) {
try {
Days days = Days.valueOf(day);
switch (days) {
case MONDAY:
System.out.println(days.name());
break;
case TUESDAY:
System.out.println(days.name());
break;
case WEDNESDAY:
System.out.println(days.name());
break;
case THURSDAY:
System.out.println(days.name());
break;
case FRIDAY:
System.out.println(days.name());
break;
case SATURDAY:
System.out.println(days.name());
break;
case SUNDAY:
System.out.println(days.name());
break;
default:
break;
}
} catch (IllegalArgumentException e) {
System.out.println("你的输入有误");
}
}
public static void main(String[] args) {
printDay("MONDAY");
printDay("TUESDAY");
printDay("WEDNESDAY");
printDay("THURSDAY");
printDay("FRIDAY");
printDay("星期一");
printDay("星期二");
printDay("星期三");
printDay("星期四");
}
}
输出结果:
image.png
因为枚举的 valueOf 方法是根据给定的名字返回相应的 enum 实例,如果不存在给定名字的实例,就会抛出异常,我们可以捕捉这个异常,对输入的格式类型进行控制。当然,从 JDK 1.7 开始 switch 已经支持 String 类型了,我们没必要用这种笨拙的方式啦。
用法三:用枚举实现单例模式。关于单例模式可以看下这篇文章
public enum Singleton {
INSTANCE;
public void doSomething(){
System.out.println("Single Dog");
}
public static void main(String[] args) {
Singleton.INSTANCE.doSomething();
}
}
可以看出,用枚举实现单例是比较简单的,而且它确保了在任何情况下(包括反序列化、反射、克隆)下都是一个单例。因为枚举类是在第一次访问时才被实例化,所以它也是懒加载的。
为什么使用枚举
可以发现,前面三种用法我们都可以不使用枚举来实现,我们为什么还要使用枚举呢?
1.首先是类型安全问题。比如用法一中的常量。
static final int MONDAY = 0;
static final int TUESDAY = 1;
这样的定义的 MONDAY 和 TUESDAY 完全可以用来做加减运算,这与我们的本意相违背,如果我们使用枚举就可以解决这种问题啦。
2.解决了意义不明确。比如调试程序时,本来想输出 MONDAY,结果输出了一个 0,如果不是自己写的完全不知道其意义。
3.代码更优雅。这个前面已经提到过了,用枚举来代替常量不仅编码更少,而且更直观。
Android 中的枚举使用
首先,Android 官方是不推荐使用枚举类型的,因为与静态常量相比,它对内存的占用要大很多(官方说是2倍),还会增大 dex 文件。这是由于枚举的最终实现是类,在编译完成后,最终为每一种类型生成一个静态对象,而在内存申请方面,对象需要的内存空间远远大于普通的静态常量。不过还是要视情况而定啦,如果代码中只有少量的枚举,我们用少量的性能损耗就可以换来了代码更直观、类型安全等好处。而且如果开启 ProGuard 优化的情况下,枚举会被转为 int 类型,所以内存占用问题是可以忽略的,不过转换时有条件的,如果枚举类存在以下的情况,则不会优化为整型:
- 枚举实现了自定义接口,并且被调用
- 代码中使用了不同签名来存储枚举
- 使用 instanceOf 关键字判断
- 对枚举强转
- 在代码中调用静态方法 valueOf 方法
- 定义可以外部访问的方法。
但是如果要对使用大量的枚举类型的项目进行优化时应该怎么做呢?这时可以使用常量加上 Android 中的 IntDef、StringDef 注解来实现类似枚举的效果。
首先,需要在项目中添加一条依赖:
compile 'com.android.support:support-annotations:latest-version'
示例:
public class MainActivity extends AppCompatActivity {
public static final int RED = 1;
public static final int GREEN = 2;
public static final int BLUE = 3;
@IntDef({RED,GREEN,BLUE})
@Retention(RetentionPolicy.SOURCE)
public @interface RGB{}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
showRGB(RED);
showRGB(GREEN);
showRGB(BLUE);
//提示错误
showRGB(1);
//提示错误
showRGB(1232);
}
public static void showRGB(@RGB int color){
System.out.println(String.valueOf(color));
}
}
网友评论