Android进阶之将注解@IntDef @StringDef替代枚举类(enum)

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;     // 女
    }

5 学习链接

Android中不使用枚举类(enum)替代为@IntDef @StringDef

猜你喜欢

转载自blog.csdn.net/chenliguan/article/details/84705465