枚举最大的作用是提供了类型安全。为了弥补Android平台不建议使用枚举的缺陷,官方推出了两个注解,IntDef和StringDef,用来提供编译期的类型检查。
首先,引入依赖包:
implementation 'com.android.support:support-annotations:27.1.0'
定义一个注解
public @interface TaskStatus {
}
是的,类似于接口的定义,加个 @ 符号。
注解一般使用步骤
那么对于上面的枚举和常量都不 work 的场景,如何使用枚举解决呢?
- 定义注解类,添加常量
@Retention(RetentionPolicy.SOURCE)
public @interface TaskStatus {
int UN_KNOW = -1;
int UN_START = 0;
int PROGRESSING = 1;
int COMPLETED = 2;
}
和接口成员一样,注解类的成员默认就是 public static final
修饰的。
@Retention(RetentionPolicy.SOURCE)
表示告诉编译器,该注解是源代码级别的,生成 class 文件的时候这个注解就被编译器自动去掉了。
doSth()
方法:
public class TaskHelper {
public static void doSth(@TaskStatus int status){
switch (status){
case TaskStatus.UN_KNOW:
//do something
break;
case TaskStatus.UN_START:
break;
case TaskStatus.PROGRESSING:
break;
case TaskStatus.COMPLETED:
break;
}
}
}
可以看出,doSth()
方法的参数是 int 类型的,但是使用 @TaskStatus
进行了注解,这样外界就无法传递 TaskStatus
之外的成员作为参数了。
- 调用
TaskHelper.doSth(TaskStatus.UN_START);
在调用时,IDE 会提示 @TaskStatus int status
,提醒我们传入 TaskStatus
类型的值。
同时,调用者如果再随便传入一个 int
值,虽然可以运行,但代码会爆红,lint 检查将会给与警告:
Must be one of: TaskStatus.UN_KNOW, TaskStatus.UN_START, TaskStatus.PROGRESSING, TaskStatus.COMPLETED
如此,保证了类型安全。但也只是警告,仍然可以运行,但总比没有警告强多了。
补充
其实这种用法在 Android 源码中屡见不鲜,比如 Resources
下的 getDrawable()
方法:
public Drawable getDrawable(@DrawableRes int id) throws NotFoundException {
final Drawable d = getDrawable(id, null);
//...
return d;
}
使用时,我们一般会这么用:
getResources().getDrawable(R.drawable.ic_launcher);
虽然是 int
值,但是当我们这么用时:
getResources().getDrawable(111);
就会爆红并提示:
Expected resource of type drawable
再补充一点,为了防止在我们定义的常量值重复,我们还可以使用Android 注解支持库(support-annotations)中的 @IntDef
来限定常量不允许重复:
@IntDef({
TaskStatus.UN_KNOW,
TaskStatus.UN_START,
TaskStatus.PROGRESSING,
TaskStatus.COMPLETED
})
@Retention(RetentionPolicy.SOURCE)
public @interface TaskStatus {
int UN_KNOW = -1;
int UN_START = 0;
int PROGRESSING = 1;
int COMPLETED = 2;
}