Typedef Annotations in Android
Java’s enum type is the standard way for defining a set of related constants. For instance, we can define an enum to represent different units of temperature:
The nice thing about an enum is that it’s type-safe. Wherever a
TemperatureUnit value is expected, only an enum value may be used:
Enums possess even more power since they can contain both methods and constructors as part of their definition. This allows them to behave similar to class types.
Before the enum type was introduced in Java 5, programmers would typically define a set of integer constants instead. Using our previous example of temperature units, we might define the constants as follows:
You’ll often find integer constants used in favor of an enum in the Android APIs. For example, the ViewGroup.LayoutParams class defines the constants
WRAP_CONTENT. The reason for this is related to performance. When compared to integer constants, enums may use more than twice the amount of memory. Since many Android devices are resource constrained, memory footprint is a legitimate concern.
The main drawback of using integer constants though is the lack of type safety. We no longer have the compiler enforcing a value to be from our set of constants. If the
setConversionUnit method is modified to accept an argument of type
int, then any integer value can be passed in:
The Android team at Google recently introduced a support library for annotations. Among the many included annotations is the new
@IntDef annotation. Its purpose is to provide type safety for traditional integer constants:
We define an annotation named
TemperatureUnit, acting as a namespace for our set of constants. We apply the
@Retention annotation to its definition with a value of
RetentionPolicy.SOURCE since we only need it to exist at compile time. Finally, we add the
@IntDef annotation with the names of our constants.
Now we can annotate our code with
@TemperatureUnit and get the added type safety:
The compiler will generate a warning in the case that one of expected constant values is not provided. While not as forceful as a compile-time error, it can be helpful in notifying both developers and continuous integration servers of type safety issues.
Sometimes it makes sense to apply more than one constant value at a time. For this, the
@IntDef annotation provides a
flag attribute, allowing multiple constant values to be combined using bitwise operators:
Now multiple constant values can be used simultaneously. When a value of combined constants is passed, the pattern is type-checked by the
@IntDef annotation. An alternative is to use the EnumSet class from the Java APIs. The issue again is the memory consumption when compared to the more lightweight integer constants.
The other typedef annotation that the support library provides is
@StringDef. While having integer constants is sufficient in most cases, treating constant values as strings can be useful. We recently made use of this in CafeJava, which provides RxJava extensions to the MobileFirst Platform API. The API allows the programmer to specify an HTTP method for a
WLResourceRequest. It expects a
String and within that class is a set of
String constants representing the different HTTP methods. CafeJava takes advantage of the
@StringDef annotation to ensure a valid constant value is passed in:
Thanks to these new annotations, we now have a way to specify sets of constants that are both performant and type-safe. You can start using them in your own code by adding the following gradle dependency: