Android 中如何使用annotion替代Enum

Posted by Suzeyu on 2016-05-20

抛弃枚举, 在android中一个更好的方法替代他.

起因


阅读原文

当我们想从一些预定义的常量中使用一个变量我们能怎么做. 我可以声明常量并给他们赋值. 我们假设某一个值是表示今天,他可以从周日到周六有不一样的值来指定,我们在java中可以这样使用整数型常量.

public class Main {
public static final int SUNDAY = 0;
public static final int MONDAY = 1;
public static final int TUESDAY = 2;
public static final int WEDNESDAY = 3;
public static final int THURSDAY = 4;
public static final int FRIDAY = 5;
public static final int SATURDAY = 6;
private int currentDay = SUNDAY;
public static void main(String[] args) {
// TODO Auto-generated method stub
Main obj = new Main();
obj.setCurrentDay(WEDNESDAY);
int today = obj.getCurrentDay();
switch (today) {
case SUNDAY:
System.out.println("Today is SUNDAY");
break;
case MONDAY:
System.out.println("Today is MONDAY");
break;
case TUESDAY:
System.out.println("Today is TUESDAY");
break;
case WEDNESDAY:
System.out.println("Today is WEDNESDAY");
break;
case THURSDAY:
System.out.println("Today is THURSDAY");
break;
case FRIDAY:
System.out.println("Today is FRIDAY");
break;
case SATURDAY:
System.out.println("Today is SATURDAY");
break;
default:
break;
}
}
public void setCurrentDay(int currentDay) {
this.currentDay = currentDay;
}
public int getCurrentDay() {
return currentDay;
}
}

问题

这段代码看似已经实现功能, 但是有问题存在的因为我可以设置任何的整数型的值给当前的currentDay.

obj.setCurrentDay(100);

方案1 使用Enum

这样即使我们设置了超出了currentDay没有实际意义值得时候, 编译器却没有给我们任何的提示. 然后在 switch语句中将错过这些值. 所以java给了我们一个解决方案就是使用枚举(Enum)和Enumerarion. 我们可以使用Enum重写之前的代码.

public class Main {
public enum WeekDays {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY
}
private WeekDays currentDay = WeekDays.SUNDAY;
public static void main(String[] args) {
// TODO Auto-generated method stub
Main obj = new Main();
obj.setCurrentDay(WeekDays.WEDNESDAY);
WeekDays today = obj.getCurrentDay();
switch (today) {
case SUNDAY:
System.out.println("Today is SUNDAY");
break;
case MONDAY:
System.out.println("Today is MONDAY");
break;
case TUESDAY:
System.out.println("Today is TUESDAY");
break;
case WEDNESDAY:
System.out.println("Today is WEDNESDAY");
break;
case THURSDAY:
System.out.println("Today is THURSDAY");
break;
case FRIDAY:
System.out.println("Today is FRIDAY");
break;
case SATURDAY:
System.out.println("Today is SATURDAY");
break;
default:
break;
}
}
public void setCurrentDay(WeekDays currentDay) {
this.currentDay = currentDay;
}
public WeekDays getCurrentDay() {
return currentDay;
}
}

这样我们的currentDay,set只接收Enum类型, 而Enum里面有什么使我们预先定义好的.这样就解决了之前出现的问题.这是一个好的解决方案.所以我们应该在以后根据场景使用它, 但是在Android中还是会有些问题的

方案2 使用android特有的魔术变量注解替代Enum

Enum in Android 枚举Enum在java中是一个完整的类. 而枚举中的每一个值在枚举类中都是一个对象. 所以在我们使用时枚举的值将比整数常量消耗更多的内存. 当我们使用枚举在安卓应用中, 如果我们的程序本身内存消耗就比较大,或者是一个游戏的应用程序. 那么我们最好使用常量来替代枚举. 但之前的问题仍然存在.

那么有什么好的办法么?

当然, Android 支持注解库中有一些好用的annotation helper 我们可以使用它们来解决我们之前的问题(在编译代码时期).

IntDefStringDef 是两个魔术变量注解. 使用这个两个来替代之前使用的Enum. 它将帮助我们在编译代码时期像Enum那样选择变量的功能. 接下来我们来使用IntDef来替换Enum看一下.

public class MainActivity extends Activity {
public static final int SUNDAY = 0;
public static final int MONDAY = 1;
public static final int TUESDAY = 2;
public static final int WEDNESDAY = 3;
public static final int THURSDAY = 4;
public static final int FRIDAY = 5;
public static final int SATURDAY = 6;
@IntDef({SUNDAY, MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY})
@Retention(RetentionPolicy.SOURCE)
public @interface WeekDays {}
@WeekDays int currentDay = SUNDAY;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setCurrentDay(WEDNESDAY);
@WeekDays int today = getCurrentDay();
switch (today){
case SUNDAY:
break;
case MONDAY:
break;
case TUESDAY:
break;
case WEDNESDAY:
break;
case THURSDAY:
break;
case FRIDAY:
break;
case SATURDAY:
break;
default:
break;
}
}
public void setCurrentDay(@WeekDays int currentDay) {
this.currentDay = currentDay;
}
@WeekDays
public int getCurrentDay() {
return currentDay;
}
}

现在我们不能直接用一个int值赋值给currentDay变量, 或者在set赋值的时候只能传入currentDay变量被注解所规定的魔术注解IntDef中的常量引用(而不是引用对应的引用值). 当我们使用赋一些没有意义的值时, 编译器将给我们一些警告让我们修改.

如果我们使用过的是Android Studio开发的话, IDE将给我们关于如果使用的建议.

创建步骤

我们可以拆解一下步骤.

首先如果想要这个特证,你应该添加 support-annotations library 到你的项目中去, 如果你使用AS,可以使用gradle的依赖.

dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
...
compile 'com.android.support:support-annotations:22.0.0'
}

如果你的项目已经依赖这个:com.android.support:appcompat-v7, 那或许不需要单独添加上述库的依赖

1.首先声明常量

public static final int SUNDAY = 0;
public static final int MONDAY = 1;
public static final int TUESDAY = 2;
public static final int WEDNESDAY = 3;
public static final int THURSDAY = 4;
public static final int FRIDAY = 5;
public static final int SATURDAY = 6;

2.创建一个注解,声明魔术变量@IntDef(或者@StringDef其实后面传入的为String类型常量)并给其添加想预定义的int常量

@IntDef({SUNDAY, MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY})
@Retention(RetentionPolicy.SOURCE)
public @interface WeekDays {}

3.给变量currentDay使用注解

@WeekDays int currentDay ;
//这时currentDay 不会接收weekDays以外的所有值, 即使是IntDef中常量名对应的值也不可以, 只能使用weekDays常量引用

4.如果我们要声明currentDay的setter和getter,可以这样使用来限制问题

public void setCurrentDay(@WeekDays int currentDay) {
this.currentDay = currentDay;
}
@WeekDays
public int getCurrentDay() {
return currentDay;
}

如果你想了解更多关于注解支持库的介绍,come here (自备梯子).