I worked too much on other stuff, so I didn’t have time to blog, so here is a tiny post.
Java annotations are pretty nice: You can annotate many things to add more information. For example, you can add an @Nullable
to a type used to tell static analyzers or IDEs that this the value of this type there might actually be null
:
public @Nullable String parse(String description) { ... return error ? null : result; }
There are many other uses, especially in adding more information needed for code generation. In working on hello-ebpf, I used annotations and generated code with JavaPoet containing annotations. When we generate the code from above with JavaPoet, it produces:
public java.lang. @Nullable String parse( java.lang.String description) { // ... }
But how could this be valid Java? I expected
public @Nullable java.lang.String parse( java.lang.String description) { // ... }
but not the former. Let’s look into the language specification. Section 4.3 tells us class types in fields and other type usages as follows:
ClassType: {Annotation} TypeIdentifier [TypeArguments] PackageName . {Annotation} TypeIdentifier [TypeArguments] ClassOrInterfaceType . {Annotation} TypeIdentifier [TypeArguments]
According to the specification @Nullable java.lang.String
and java.lang. @Nullable String
are the same.
It gets even weirder with arrays:
java.lang. @Nullable Integer @Nullable [] arr @Nullable []
This denotes a two-dimensional array of strings that might be null and might contain null, and its arrays might contain null. This is true to the language specification:
ArrayType: PrimitiveType Dims ClassOrInterfaceType Dims TypeVariable Dims Dims: {Annotation} [ ] {{Annotation} [ ]}
There is even an example in the specification that is similar to our example:
For example, given the field declaration:
@Foo int f;
@Foo
is a declaration annotation onf
ifFoo
is meta-annotated by@Target(ElementType.FIELD)
, and a type annotation onint
ifFoo
is meta-annotated by@Target(ElementType.TYPE_USE)
. It is possible for@Foo
to be both a declaration annotation and a type annotation simultaneously.Type annotations can apply to an array type or any component type thereof (§10.1). For example, assuming that
A
,B
, andC
are annotation interfaces meta-annotated with@Target(ElementType.TYPE_USE)
, then given the field declaration:@C int @A [] @B [] f;
@A
applies to the array typeint
[]
[]
,@B
applies to its component typeint
[]
, and@C
applies to the element typeint
. For more examples, see §10.2.An important property of this syntax is that, in two declarations that differ only in the number of array levels, the annotations to the left of the type refer to the same type. For example,
@C
applies to the typeint
in all of the following declarations:@C int f; @C int[] f; @C int[][] f;Language Specification Section 9.7.4
Conclusion
Java never stops surprising me. This syntax looked weird when I first stumbled upon it, but after looking through the language specification, I see how useful and justified this placement of annotations is.
I hope you enjoyed this tiny blog post on annotations; see you in my next one.
P.S.: I’m currently at KCDC…