ENUMS VS ANNOTATIONS IN ANDROID

Anmol Sehgal
4 min readJun 18, 2018

--

In android enums are not considered as a “Best-practise” to be used because of memory issues, as every enum type is an object of that class. So Objects are created for enum types everytime, even incases where simple primitive types(int/double) could have be used.
So we must use annotations wherever we need to take advantage of enums. Before we move to how to use annotations in android to replace enums, a short overview on enums.
You can move to the section: “In Android” if you are well aware of basic enum concepts.

Use of enums:

An enum type is a special data type that enables for a variable to be a set of predefined constants. The variable must be equal to one of the values that have been predefined for it.

e.g. we have only 2 boolean values i.e. either true or false. And we want no other value other than these so we can predefine them like:

Without enums i.e. using constants:

Create a constant class containing boolean constants like:

public class Boolean{
public static final int TRUE= 1;
public static final int FALSE= 0;
}
public class EnumTest {
int booleanValue;
public EnumTest(int booleanValue) {
this.booleanValue = booleanValue;
}
public void trueOrFalse() {
switch (booleanValue) {
case Boolean.TRUE:
System.out.println("value true");
break;
case Boolean.FALSE:
System.out.println("value false");
break;
default:
System.out.println("invalid boolean value");
break;
}
}
public static void main(String[] args) {
EnumTest value= new EnumTest(Boolean.TRUE);
value.trueOrFalse();
}
}

The output is:

value is true

But the problem of using constants is that we can have any other value of boolean as well,
e.g. we can even assign a value of 2 to boolean(as the function parameter expects any integer value) as:

public static void main(String[] args) {
EnumTest value = new EnumTest(2);
value.trueOrFalse();
}

then boolean has value “2” which is not recognized as a valid boolean value, and hence here the output will be:

Invalid boolean value

So we want that boolean can be no other than TRUE or FALSE.

With Enums:

public enum Boolean{
TRUE , FALSE
}

Now the enum named Boolean can have only TRUE or FALSE vales, and there can be no value other than these defined.

public class EnumTest {
Boolean booleanValue;
public EnumTest(Boolean booleanValue) {
this.booleanValue = booleanValue;
}
public void trueOrFalse() {
switch (booleanValue) {
case TRUE:
System.out.println("valuerue");
break;
case FALSE:
System.out.println("valuealse");
break;
default:
System.out.println("invalidean value");
break;
}
}
public static void main(String[] args) {
EnumTest value= new EnumTest(Boolean.TRUE);
value.trueOrFalse();
}
}

The output is:

value is true

And here we can have no value of Boolean, So this solves our problem.

In Android:

Enum is a special kind of Java class. Every value of Enum is an object of that Enum class. So Enum values will take more memory then int constant we used in the example. We can use Enum in Android Applications, but for applications intended to use less memory, we better use integer constants instead of Enum. But then the problem remains.

Annotations save us from this problem. Android support annotation library can be used which will handle this problem at compile time itself. 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 does) in compile time. Following code shows how to use IntDef instead of Enum.

To use this feature in android add the support-annotations dependency to your project by putting the following line in the dependencies block of your build.gradle file:

dependencies { compile 'com.android.support:support-annotations:24.2.0' }

Now annotations are enabled in our project.

public class EnumTest {// make annotation @BooleanValue
// @IntDef denotes that the element is of integer type and
// its value should be one of the explicitly named constants
//(TRUE or FALSE)
@Retention(SOURCE)
@IntDef({BooleanValue.TRUE, BooleanValue.FALSE})
public @interface BooleanValue {
int FALSE = 0;
int TRUE = 1;
}
// annotation on the int booleanValue means that booleanValue
// can have values permitted by annotation @booleanValue
//i.e. BooleanValue.TRUE or BooleanValue.FALSE only
@BooleanValue
int booleanValue;
// this annotation can also be used inside method parameter so
// that method parameter can only take TRUE or FALSE value
public EnumTest(@BooleanValue int booleanValue) {
this.booleanValue = booleanValue;
// setting this.booleanValue = 9;
// will give compile error that
// "Must be one of BooleanValue.TRUE or BooleanValue.FALSE"
}
public void trueOrFalse() {
switch (booleanValue) {
case BooleanValue.TRUE:
System.out.println("value true");
break;
case BooleanValue.FALSE:
System.out.println("value false");
break;
default:
System.out.println("invalid boolean value");
break;
}
}
public static void main(String[] args) {
EnumTest value = new EnumTest(BooleanValue.FALSE);
value.trueOrFalse();
}
// Annotation over the method means that method can only
// return BooleanValue.TRUE or BooleanValue.FALSE
@BooleanValue
int getBooleanValue() {
return BooleanValue.FALSE;
}
}

Now as comments mention, we created the annotation @BooleanValue which permits integer values TRUE or FALSE only, and it uses @IntDef meaning it allows only integer values which are mentioned in under it e.g. @IntDef({BooleanValue.TRUE, BooleanValue.FALSE}) means only BooleanValue.TRUE and BooleanValue.FALSE will be treated as the BooleanValue which checking against the @BooleanValue type.

@Retention indicates how long annotations with the annotated type are to be retained and here retention policy is set to SOURCE means this annotation is used by the compiler and are not needed after compilation is done, so they aren’t written to the bytecode.

And this annotation can be used with the method:

@BooleanValue
int getBooleanValue() {
return BooleanValue.FALSE;
}

to confirm that method returns only one of these values.

And over a variable declaration:

    @BooleanValue
int booleanValue;

to confirm that variable can have only one of these values.

Or inside the method/constructor parameter:

public EnumTest(@BooleanValue int booleanValue) {

or else there will be a compilation error like we had in the case of enums.

So instead of having enums restricting the type of values allowed, we can have annotations so that only PERMITTED values can be used, and these can be used anywhere enums can be used.

Similarly, @StringDef can be used which will denote the String values

@Retention(SOURCE)
@StringDef({BooleanValue.TRUE, BooleanValue.FALSE})
public @interface BooleanValue {
String FALSE = "false";
String TRUE = "true";
}

and now @BooleanValue will give a compilation error if the element/method it is associated with has a value other than BooleanValue.TRUE which is “true” or BooleanValue.FALSE which is “false”.

--

--