Annotations are just modifiers in Java (syntactically speaking)

Annotations add pieces of information to elements of a Java program.
We can annotate a class declaration, a method declaration, a field declaration, and other declarations.
We can also annotate uses of a type, such as the type in a throws
clause.
The annotations in a Java program are typically inspected by a tool, such as the Java compiler, or by a library.
While tools and libraries may assign meaning to annotations, to the Java language's grammar, and when used on declarations, annotations are no more than element modifiers. This is what we'll discuss in this blog post.
Let's begin.
Example
When annotating a program element, a method declaration for example, it is common to write the annotation at the very start of the declaration:
@Overridepublic final String toString() { return "Annotations are just modifiers";}
See how the annotation sits on top of the method in the example?
I always thought that was the only possible way to annotate a method.
It turns out I was wrong; the Override
annotation can go between the public
and the final
modifiers:
public @Override final String toString() { return "Annotations are just modifiers";}
Or, if you prefer, the Override
annotation can go just before the return type of the method:
public final @Override String toString() { return "Annotations are just modifiers";}
I used to think that the last two examples would fail to compile, but they do not; all three declarations are syntactically correct and they are all equivalent.
It's all in the JLS
It is all specified in the Java Language Specification (JLS).
Method declarations are defined in Section 8.4.
This section defines a number of productions.
We are interested in the MethodDeclaration
production:
MethodDeclaration:
{MethodModifier} MethodHeader MethodBody
The curly brackets indicate that the enclosed content can occur "zero or more" times.
So the MethodDeclaration
production specifies that a method declaration begins with zero or more method modifiers.
A little later, in Section 8.4.3,
we find the definition for method modifiers.
It defines the MethodModifier
production:
MethodModifier:
(one of)
Annotation public protected private
abstract static final synchronized native strictfp
See how Annotation
is included here?
In other words, all the listed items are considered method modifiers by the language's grammar,
and, as shown in the examples from the previous section, they can appear in any order.
It is the same with other declarations
It works the same with other declarations:
-
Class declarations (including enum declarations and record declarations)
-
Interface declarations (including annotation interface declarations)
-
Constructor declarations
-
Field declarations
-
Formal parameter declarations
-
Local variable declarations
As an example, let's look at annotation interface declarations. They are defined in Section 9.6 of the JLS:
AnnotationInterfaceDeclaration:
{InterfaceModifier} @ interface TypeIdentifier AnnotationInterfaceBody
Modifiers come before the '@
' character.
This production reuses the (regular) interface modifiers defined in Section 9.1.1:
InterfaceModifier:
(one of)
Annotation public protected private
abstract static sealed non-sealed strictfp
Just like the method declaration modifiers, the interface declaration modifiers production includes the Annotation
production.
Therefore, the following annotation declaration:
@Retention(RetentionPolicy.SOURCE)@Target({ ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD})public @interface MyAnnotation {}
Is equivalent to the following declaration:
@Retention(RetentionPolicy.SOURCE)public @Target({ ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD}) @interface MyAnnotation {}
Here, the public
modifier was placed between the Retention
and Target
annotations.
It compiles without errors.
Just because you can doesn't mean you should
As a recommendation, it’s best to:
-
Write declaration annotations before all other modifiers.
-
Write type annotations immediately before the type they apply to.
For example, consider this field declaration:
@First@Secondprivate final @Third String value;
If our codebase follows idiomatic conventions, we can safely assume that:
-
The
@First
and@Second
annotations apply to the field declaration. -
The
@Third
annotation applies to the type of the field.
This guidance is even spelled out in Section 9.7.4 of the JLS, titled "Where Annotations May Appear":
"It is customary, though not required, to write declaration annotations before all other modifiers, and type annotations immediately before the type to which they apply."
Following this convention makes it clearer to readers what each annotation targets.
Conclusion
To the Java compiler, annotations are just modifiers. So, in a method declaration, for example, an annotation may appear before, after, or in between the regular modifiers.
However, just because you can write annotations in any position relative to the other modifiers, it doesn't mean that you should. It’s best to follow the idiomatic forms of the language:
-
Write declaration annotations before all other modifiers.
-
Write type annotations immediately before the type they apply to.
While the position of annotations may be irrelevant to the Java compiler, it matters to the readers of our code. And writing readable code should always be our priority.