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的限制,如图:
Android进阶之将注解@IntDef @StringDef替代枚举类(enum)_第1张图片
(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()方法的时候,传入不在限定之内的值,那么编译就不会通过,有错误提示,如图:
Android进阶之将注解@IntDef @StringDef替代枚举类(enum)_第2张图片

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

你可能感兴趣的:(Android进阶)