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:
- stick to the canonical way unless your annotation is internal to your code and you do not refer to it from anywhere else, including tests