ENUM是什么,为什么使用它

Enum是Java中包含固定常量的数据类型。当需要使用预先定制的几个值,这几个值表示一些数据类,这时我们可以用ENUM。在一些可能的值中取一个值时很适合用Enums。例如:

public enum Season
{  
    WINTER, SPRING, SUMMER, FALL 
}

不用IntegerString常量,我们一般用ENUM可以在编译时进行检查,避免传入不合法的参数。

使用ENUM的缺陷

ENUM中的每一个值都是一个Object,它的每个声明都会占用运行时的部分内存以便能够引用到这个Object。因此ENUM的值会比对应的IntegerString所占用的内存多。在Android之前的版本中(<=2.2),存在着关于ENUM引起的性能问题,这个问题在JIT编译器中解决了。

添加一个ENUM将会增大最终的DEX文件(Integer常量的13倍大)。并且会引起运行时的过度开销,你的应用也会占用更多的空间。

因此过度在Android开发中使用ENUM将会增大DEX大小,并会增大运行时的内存分配大小

如果你的应用是使用了许多的ENUM,那么使用Integer或者String常量会更好。但问题仍然存在…

解决办法

Android 提供了注解库,其中有TypeDef注解。这些注解能够确保一个特定的参数,返回值或者字段能够在特别一组常量中引用一个。它们能确保自动完成允许的常量中选择一个。

IntDefStringDef是两个神奇的注解常量,可以用来替代Enum的使用。这些注解能够帮助我们在编译时对变量赋值进行检查。

如何使用?

我们从一个简单的例子中去明白如何使用。下面的代码段是关于ConstantSeason 类的。

public class ConstantSeason {
    public static final int WINTER = 0;
    public static final int SPRING = 1;
    public static final int SUMMER = 2;
    public static final int FALL = 3;

    public ConstantSeason(int season) {
        System.out.println("Season :" + season);
    }

    public static void main(String[] args) {
        // Here chance to paas invalid value 
        ConstantSeason constantSeason = new ConstantSeason(5);
    }
}

不幸的是,这里不法确保用户能够输入正确的值–这样使用没有类型保证。

同时使用ENUM来实现将会是如下:

public class EnumSeason {

    public EnumSeason(Season season) {
        System.out.println("Season :" + season);
    }

    public enum Season {
        WINTER, SPRING, SUMMER, FALL
    }

    public static void main(String[] args) {
        EnumSeason enumSeason = new EnumSeason(Season.SPRING);
    }
}

现在让我们瞧瞧神奇的注解常量是如何解决的。

添加支持注解的依赖到你的项目中,需要在build.gradle文件中的依赖块中添加:
dependencies { compile 'com.android.support:support-annotations:24.2.0' }

声明常量和@IntDef

// Constants
public static final int WINTER = 0;
public static final int SPRING = 1;
public static final int SUMMER = 2;
public static final int FALL = 3;

// Declare the @IntDef for these constants
@IntDef({WINTER, SPRING, SUMMER, FALL})
@Retention(RetentionPolicy.SOURCE)
public @interface Season {}

这里TypeDef注解使用了@interface来声明新的枚举注解类型。其中@IntDef@StringDef注解以及@Retention标注了新的注解,目的是定义这个枚举类型。而@Retentino(RententionPolicy.SOURCE)注解告诉编译器在生成.class文件时不保留枚举注解数据。

因此实现以上的例子将会是:

public class AnnotationSeason {

    public static final int WINTER = 0;
    public static final int SPRING = 1;
    public static final int SUMMER = 2;
    public static final int FALL = 3;

    public AnnotationSeason(@Season int season) {
        System.out.println("Season :" + season);
    }

    @IntDef({WINTER, SPRING, SUMMER, FALL})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Season {
    }

    public static void main(String[] args) {
        AnnotationSeason annotationSeason = new AnnotationSeason(SPRING);
    }
}

现在传入一些不同于Season的值时,编译器将会报错。

@StringDef可以以同样的方式使用。

// Constants
public static final String WINTER = "Winter";
public static final String SPRING = "Spring";
public static final String SUMMER = "Summer";
public static final String FALL = "Fall";

// Declare the @ StringDef for these constants:
@ StringDef ({WINTER, SPRING, SUMMER, FALL})
@Retention(RetentionPolicy.SOURCE)
public @interface Season {}

下面是是Android性能关于Enum代价的视频(注意Youtube,如何查看请自行解决)

width="700" height="393" src="https://www.youtube.com/embed/Hzs6OBcvNQE" allowfullscreen="">

结论

Enums比起使用普通常量会至增加2倍以上bytes到最终的APK大小,5到10倍更多的RAM。这个文章是一个关于性能优化最佳实践的应用。你可以参考学习更多关于support annotation library