Intelligent constants in your Android App
We use constants everywhere in our projects. Especially in Android they are used quite extensively. Have you ever wondered to give those dumb constants some intelligence (or Intellisense)? Let’s find out!
Consider a scenario where you want to pass a flag to a method and perform something based on the value passed. For example, consider a method which take three arguments. First and second ones being the numbers and the third one an operation to perform on those two. It would seem something like this.
private int performOperation(int a, int b, <operation>)
What data type would you choose for <operation> argument?
Enums
The first thing that might come into mind is an Enum. Enums are good and packed with many features in Java.
public enum Operations {
ADD, SUBTRACT, DIVIDE, MULTIPLY
}private int performOperation(int a, int b, Operations operation) { }someFunction() {
performOperation(1, 2, Operations.ADD);
}
Even though Enums does provide a cleaner readability and ease while calling the method from other places, they are quite heavy (especially for Android OS with limited resources). Enums in Java are feature packed and almost similar to classes where you can have methods (both concrete and abstract), easy conversion from String to Enum objects and many more. But in this case, we are just using them as constants.
Simple Constants
So instead of Enums, we can go with the option of simple constants.
public static final int ADD = 0;
public static final int SUBTRACT = 1;
public static final int DIVIDE = 2;
public static final int MULTIPLY = 3;private int performOperation(int a, int b, int operation) { }
This way, we can make our app lighter but at the expense of readability and ease of usage. The caller of the method doesn’t know what integer values to pass.
Intelligent Constants
Let’s regain the readability and ease of usage we lost when we switched from Enums to constants.
Android’s Annotations to the rescue. Android provides two annotations to help us here. @IntDef and @StringDef helps us adding that intelligence to our constants.
// Constants
public static final int ADD = 0;
public static final int SUBTRACT = 1;
public static final int DIVIDE = 2;
public static final int MULTIPLY = 3;// Bundling them under one definition
@Retention(RetentionPolicy.SOURCE)
@IntDef({ADD, SUBTRACT, DIVIDE, MULTIPLY})
public @interface OperationsDef { }private int performOperation(int a, int b, @OperationsDef int operation) { }
Here we create a definition for our constants OperationsDef. Then annotate the third parameter of our function with this definition. That’s it!
We get our ease of usage back when calling this function. Additionally compiler complains if any integer other than the ones included in our definition were passed. Wonderful! Isn’t it?
These annotations were extensively used in the Android APIs. For example, you might have used View.VISIBLE, View.GONE, View.INVISIBLE hundreds of times. These are @IntDef values.
public static final int VISIBLE = 0x00000000;
public static final int INVISIBLE = 0x00000004;
public static final int GONE = 0x00000008;@IntDef({VISIBLE, INVISIBLE, GONE})
@Retention(RetentionPolicy.SOURCE)
public @interface Visibility { }
For more details on the negative effects of enums on your Android App, see the video below.