I'm trying to implement @IntDef
annotation in Android development.
First Method: it looks great with the definition separated in a Constant.java
class:
public class Constant {
@IntDef(value={SORT_PRICE, SORT_TIME, SORT_DURATION})
@Retention(RetentionPolicy.SOURCE)
public @interface SortType{}
public static final int SORT_PRICE = 0;
public static final int SORT_TIME = 1;
public static final int SORT_DURATION = 2;
}
Usage:
@Constant.SortType int sortType = Constant.SORT_PRICE;
But things get a lot messier when there's multiple definition (e.g UserType, StoreType, etc) in one file.
Second Method: So I came up with something like this to separate values between definition:
public class Constant {
@IntDef(value={SortType.SORT_PRICE, SortType.SORT_TIME, SortType.SORT_DURATION})
@Retention(RetentionPolicy.SOURCE)
public @interface SortTypeDef{}
public static class SortType{
public static final int PRICE = 0;
public static final int TIME = 1;
public static final int DURATION = 2;
}
}
Usage:
@Constant.SortTypeDef int sortType = Constant.SortType.PRICE;
But as you can see, I created two different name for it: SortTypeDef
and SortType
Third Method: I tried to move the list of possible values inside @interface
:
public class Constant {
@IntDef(value={SortType.SORT_PRICE, SortType.SORT_TIME, SortType.SORT_DURATION})
@Retention(RetentionPolicy.SOURCE)
public @interface SortType{
int PRICE = 0;
int TIME = 1;
int DURATION = 2;
}
}
Usage
@Constant.SortType int sortType = Constant.SortType.PRICE;
While it does work, I don't know what is the drawback.
Is it okay to put the possible values of @IntDef
inside @interface
? Is there any performance differences across the three methods above?
Short answer: for simple projects, it is OK, but for more complex ones the first method is preferred.
Long answer:
Although bytecode for sortType
is identical in all three cases, there is a difference. The key lies in the Retention
annotation, which sets retention policy to SOURCE
. That means that your SortType
annotation is "to be discarded by the compiler", so bytecode for annotation itself is not generated.
First method defines regular static fields outside the annotations, with the regular bytecode generated for them. Second and third cases define constants within annotations, and bytecode for the constants is not generated.
If compiler has access to the source file containing your SortType
declaration, either method is fine and bytecode for sortType
is identical. But if source code is not accessible (e.g. you have only compiled library), annotation is not accessible. For the first approach, only annotation itself is not accessible, but for the latter ones, constants values are not accessible too.
I used to prefer the third method as the most clean and structured. I used to until one day I ran into an issue: when I started writing Espresso tests for that code, compiler did not have access to the source code defining the annotation. I had to either switch to the canonical IntDef
declaration or to use integer values instead of symbolic constants for the test.
So the bottom line is:
To make your third method working, you should name values
like in the interface.
I used your code and make it works:
public class Constant {
@IntDef(value = {SortType.PRICE, SortType.TIME, SortType.DURATION})
@Retention(RetentionPolicy.SOURCE)
@interface SortType {
int PRICE = 0;
int TIME = 1;
int DURATION = 2;
}
}
Or
public class Constant {
@IntDef(value = {SortType.SORT_PRICE, SortType.SORT_TIME, SortType.SORT_DURATION})
@Retention(RetentionPolicy.SOURCE)
@interface SortType {
int SORT_PRICE = 0;
int SORT_TIME = 1;
int SORT_DURATION = 2;
}
}
Usage for second:
@Constant.SortType int sortType = Constant.SortType.SORT_DURATION;
Pick one, both should work.
I came here hoping to find why the Android docs show your first method, but the third method has been working fine for me in production code for months. I haven't seen any reason not to do it that way. As you said, it cleans up the namespace when you might have multiple sets of related constants.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With