1 概述
Enum是java中一种包含固定常数的类型。当我们需要预先定义一些值,并限定范围时,使用 Enum,来做到编写和编译都查错。
Java的Enum的实质是特殊单例的静态成员变量,可以在编写器,编译器做到各种静态检查防呆;在运行期,所有枚举类作为单例,全部加载到内存中。
因此,Enum增加了APK的内存占用,比常量多5到10倍的内存占用。所以放弃枚举,就是关于安卓应用性能的内存占用部分的最佳实践方法之一。
2 为什么要使用枚举
2.1 例子
public class SexTest {
private final int MAN = 101;
private final int WOMEN = 102;
private int sex;
//设置性别
private void setSex(int sex) {
this.sex = sex;
}
// 获取性别
private String getSex() {
if (MAN == sex) {
return "男";
} else if (WOMEN == sex) {
return "女";
} else {
return "未知";
}
}
public void main() {
// 设置为101入参,而非限定的MAN
setSex(101);
String sex = getSex();
System.out.println("sex: " + sex); // 输出:sex: 男
// 设置为102入参
setSex(102);
String resultSex = getSex();
System.out.println("resultSex: " + resultSex); // 输出:resultSex: 未知
}
}
2.2 缺点
当我们定义了一个男女的final整型作为入参时,不一定保证入参的都是我们想要的入参,这里就有一个 类型不安全的问题出现,而枚举就可以解决这个问题。
3 枚举类
3.1 例子
public class SexEnumTest {
public enum Sex {
MAN, WOMEN
}
private Sex sex;
// 设置性别
private void setSex(Sex sex) {
this.sex = sex;
}
// 获取性别
private String getSex() {
if (MAN == sex) {
return "男";
} else if (WOMEN == sex) {
return "女";
} else {
return "未知";
}
}
public void main() {
// 这里的入参必须为Sex枚举类中的其中一个枚举常量
// 绝对不允许输入没有再Sex枚举里面定义的常量
setSex(Sex.MAN);
// 错误
setSex(101);
String resultSex = getSex();
System.out.println("resultSex: " + resultSex); //out:resultSex: 男
}
}
3.2 优点
(1)利用枚举,在setSex()方法里面对入参做了枚举Sex的限制,如图:
(2)对于想输入任何非枚举类Sex里面定义的枚举常量,编译都是不能通过的;
(3)这就很好的限制了入参混乱的问题。
3.3 缺点
(1)每一个枚举值都是一个单例对象,在使用它时会增加额外的内存消耗,所以枚举相比与Integer和String会占用更多的内存;
(2)较多的使用Enum会增加DEX文件的大小,会造成运行时更多的IO开销,使我们的应用需要更多的空间。特别是分dex多的大型APP,枚举的初始化很容易导致ANR。
4 不使用枚举类型,使用@IntDef/@StringDef + @interface的解决方案
既然是因为参数的类型太泛了造成的类型不安全,那么我只要将参数限定在某一个类型集合里面,用@IntDef/@StringDef + @interface来进行限定参数。
4.1 例子
public class SexIntDef {
// 表示开启Doc文档
@Documented
@IntDef({SEX.UNKNOWN, SEX.MALE, SEX.FEMALE})
// 表示注解作用范围,参数注解,成员注解,方法注解
@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
// 表示注解属于(编译)级别,仅存在于源码中,在class字节码文件中不包含。
@Retention(RetentionPolicy.SOURCE)
// 接口,定义新的注解类型
public @interface SEX {
int UNKNOWN = 0; // 未知(保密)
int MALE = 1; // 男
int FEMALE = 2; // 女
}
private @SEX int sex = SEX.MALE;
private @SEX int sex1 = 101; // 错误
private void setSex(@SEX int sex){
this.sex = sex;
}
public void main() {
// 绝对不允许输入没有在@IntDef/@StringDef + @interface限定的参数
setSex(SEX.MALE);
// 错误
setSex(1);
}
}
4.2 优点
(1)如果我们尝试在调用setSex()方法的时候,传入不在限定之内的值,那么编译就不会通过,有错误提示,如图:
4.3 同理,我们也可以使用@StringDef
4.4 常用模式
// 性别
@IntDef({SEX.UNKNOWN, SEX.MALE, SEX.FEMALE})
@Retention(RetentionPolicy.SOURCE)
public @interface SEX {
int UNKNOWN = 0; // 未知(保密)
int MALE = 1; // 男
int FEMALE = 2; // 女
}