Java Enum and Android IntDef/StringDef annotation

When we want a variable x to have values from some predefined constants then what can we do. We can declare the constant variables and set x from these constants. Lets assume x is currentDay and it can have any value from sunday to friday. We can code like this in java using integer constants.

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

}

Problem with this code is I can set any integer value to currentDay.

obj.setCurrentDay(100);

and compiler will not give me any error. Then in switch/case we will miss that value. So Java gives us a solution that is Enumeration or Enum. If We rewrite the code with Enum in Java.

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

}

Now we have type safety. I can not have any value other then WeekDays in currentDay variable. That is a great improvement, and we should use it. But in Android there is a problem.

Enum in Android: Enum is a full fledged class in Java. Every value of Enum is an object of that Enum class. So Enum values will take more memory then int constant we used earlier. Even in old android devices (<=2.2), there were some performance issue related to Enum which were solved in JIT compiler. Now we can use Enum in Android Application, but if our application is a memory hungry application or a game application then we better use integer constants instead of Enum. But then problem remains.

There is a solution. Android support annotation library has some good annotation helper which can be used to find bug earlier (in compile time). IntDef and StringDef are two Magic Constant Annotation which can be used instead of Enum. These will help us to check variable assignment like Enum in compile time. Following code showed how to use IntDef instead of 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;
    }
}

Now we can not set any value other than WeekDays in currentDay or today variable. Compiler will check it and will give error. And if we use Android Studio, IDE will give suggestion about the value. So how to use it. At first declare the constants.

    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;

Then declare the @IntDef for these constants,

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

To set a variable of WeekDays type, so that no value other than weekdays can be set to that variable, we declare it like following,

@WeekDays int currentDay ;

Now if there is any assignment which set currentDay to any other value than WeekDays, compiler will give a error. To set function parameter and function return type with WeekDays type,

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

    @WeekDays
    public int getCurrentDay() {
        return currentDay;
    }

@StringDef can be used in same manner.

public class MainActivity extends Activity {

    public static final String SUNDAY = "sunday";
    public static final String MONDAY = "monday";
    public static final String TUESDAY = "tuesday";
    public static final String WEDNESDAY = "wednesday";
    public static final String THURSDAY = "thursday";
    public static final String FRIDAY = "friday";
    public static final String SATURDAY = "saturday";


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


    @WeekDays String currentDay = SUNDAY;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setCurrentDay(WEDNESDAY);

        @WeekDays String 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 String currentDay) {
        this.currentDay = currentDay;
    }

    @WeekDays
    public String getCurrentDay() {
        return currentDay;
    }
}

To use this feature, you have to add support-annotations library to your project. If you use Android Studio, then add following in your Gradle dependency.

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

You can learn more about support annotation library here.  And below is android performance patterns video about price of Enum.

Advertisements
Java Enum and Android IntDef/StringDef annotation

8 thoughts on “Java Enum and Android IntDef/StringDef annotation

    1. It seems it only gives error compare using “==”. It raise error for a method if parameter of that method is annotated. equals is a method of string class which is not annotated that’s why I think it is not giving any error, but it should because this annotation is specially for String class. I think google should add validation check for equals method. Though you can compare using “==”, which we actually use for Enum.

  1. Chris Ruddell says:

    What is the performance impact of using constants defined in an interface compared to enums and intdef’s?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s