Sign in Get started
ARCHIVE WRIT E FOR US S T YLE GUIDE ABOUT JOB BOARD
You have 2 free stories left this month. Sign up and get an extra one for free.
Java Annotations Explained
How to add and use metadata in our code
Ben Weidig Follow
Jan 27 · 6 min read
Photo by Jasmin Ne on Unsplash
“an·no·ta·tion | a-nə-ˈtā-shən
1: a note added by way of comment or explanation”
— Merriam-Webster
With JSR-175, Java 5 gained a metadata facility, allowing us to annotate
our code with decorative syntactic metadata.
This metadata can be provided for types, fields, methods, parameters,
constructors, local variables, type parameters, usage of types, and even
other annotation types. It can be used at different steps of our code’s
lifecycle by a wide arrangement of tools.
. . .
Anatomy of Annotations
The basic definition of an annotation type is simple:
1 @Retention(RetentionPolicy.RUNTIME)
2 @Target(ElementType.TYPE)
3 @Inherited
4 @Documented
5 public @interface MyAnnotation {
6 String name() default "";
7 int value();
8 }
medium-2020-java-annotations-explained-01.java hosted with ❤ by GitHub view raw
Let’s go through it line-by-line, everything will be explained in detail
further down:
1: @Retention — In which lifecycle of our code the annotation will be
available.
2: @Target — Where we can we use the annotation.
3: @Inherited — If present, an annotated type will pass it on to any
subtypes.
4: @Documented — If present, documentation tools like javadoc can
access it.
5: @interface — Marks an annotation type.
6–7: Values of the annotation, optionally with a default value.
Basic usage
The simplest annotation use would be @MyAnnotation at a compatible
target site.
But annotations can have multiple values that might be required to be
set, if no default value is provided. The value name value() is a special
one. It can be used without a name if no other values are present.
1 // Both values are specified, naming them is required
2 @MyAnnotation(name = "an awesome name", value = 42)
3 public class MyType { ... }
4
5 // Only "value()" is present, "name()" will be its default value
6 @MyAnnotation(value = 42)
7 public class MyType2 { ... }
8
9 // Only "value()" needed, we can actually omit the name
10 @MyAnnotation(42)
11 public class MyType3 { ... }
medium-2020-java-annotations-explained-02.java hosted with ❤ by GitHub view raw
@Retention
The typical lifecycle of our code is as follows:
Source Code
▼
▼ ◁ Compiler
▼
Class file
▼
▼ ◁ JVM
▼
Runtime
The retention policy of annotations reflects these lifecycles and provides
us with a way to specify the exact availability of metadata:
RetentionPolicy.SOURCE
Annotations are only available in the source. The compiler will
discard the metadata, so neither compiler nor runtime has access to
it. This retention policy is useful for pre-compile tools, like annotation
processors.
RetentionPolicy.CLASS
The default retention policy. Annotations are visible to the compiler,
and will be available in the class files, but not at runtime. Any post-
compile byte-code tools might use the metadata.
RetentionPolicy.RUNTIME
All metadata will be available at runtime.
Which retention policy we need for our custom annotations depends on
our requirements.
The provided metadata might contain sensitive information on the inner
workings of the annotated code. We should always choose the lowest
retention possible for our code to still work.
@Target
Not every annotation makes sense on every available target. That’s why
we can explicitly set the acceptable targets. The eight available targets
are defined in java.lang.annotation.ElementType :
ElementType.PACKAGE — Package declarations.
ElementType.TYPE — Classes, interfaces, enum.
ElementType.TYPE_PARAMETER — Generic type parameters. Available
since Java 8.
ElementType.TYPE_USE — Any usage of a type, like declarations, generic
parameters, or casts. Available since Java 8.
ElementType.ANNOTATION_TYPE — Annotation types.
ElementType.CONSTRUCTOR — Constructor declaration.
ElementType.FIELD — Fields and enum constants.
ElementType.METHOD — Method declarations.
ElementType.LOCAL_VARIABLE — Local variable declarations (not
retained in class files or at runtime).
The @Target annotation accepts an array of targets:
1 // Multi-Target
2 @Target({ ElementType.FIELD, ElementType.Type })
3
4 // Single-Target
5 @Target(ElementType.ANNOTATION_TYPE)
medium-2020-java-annotations-explained-03.java hosted with ❤ by GitHub view raw
If @Target is not specified, the annotation defaults to every available
ElementType , except ElementType.TYPE_PARAMETER .
@Inherited
Annotations are not inherited by default. By adding @Inherited to an
annotation type, we allow it to be inherited. This only applies to
annotated type declarations, which will pass it down to their subtypes.
1 @MyAnnotation(value = 42)
2 public class MyType { ... }
3
4 // Any annotation check at runtime
5 // will also provide "MyAnnotation" and its value 42
6 public class MySubType extends MyType { ... }
medium-2020-java-annotations-explained-04.java hosted with ❤ by GitHub view raw
@Documented
Java default behavior for documentation is to ignore any annotation.
With @Documented we can change this, making the metadata and its values
accessible through documentation.
@Repeatable
Until Java 8, we could apply a specific annotation type only once on a
target. With the help of the annotation @Repeatable , we can now declare
an annotation repeatable by providing an intermediate annotation:
1 public @interface MyRepeatableAnnotationContainer {
2 MyRepeatableAnnotation[] value();
3 }
4
5 @Repeatable(MyRepeatableAnnotationContainer.class)
6 public @interface MyRepeatableAnnotation {
7 String value() default "";
8 }
medium-2020-java-annotations-explained-05.java hosted with ❤ by GitHub view raw
Now we can use our annotation more than once:
1 @MyRepeatableAnnotation
2 @MyRepeatableAnnotation("Foo")
3 @MyRepeatableAnnotation("Bar")
4 public class MyType { ... }
medium-2020-java-annotations-explained-06.java hosted with ❤ by GitHub view raw
. . .
Annotation Values
Being able to annotate our code and check if the annotation is present at
different lifecycle events is great. But providing additional values besides
the annotation type itself is even better. And even default values are
supported.
Values are optional, separating annotations into two groups:
Marker — No values. The mere presence is the actual metadata.
Examples: @Documented , @Inherited , @Override .
Configuration — Values present, maybe with default values for less
typing when used. Examples: @Target , @Retention .
The Java Language Specification (JLS) splits Configuration into Normal
Annotation and Single Element Annotation. But in my opinion, the
behavior of those two overlaps enough to be treated as (almost) equal.
Configuration annotations support multiple values. The allowed types
are defined in the JLS 9.6.1:
Primitive types
String
The type Class or Class<T>
Enum types
Annotation types
Array of any preceding type (single-dimension only)
Arrays are handled uniquely. If only a single value is provided when used,
we can omit the curly braces.
1 @Retention(RetentionPolicy.RUNTIME)
2 @Target(ElementType.TYPE)
3 public @interface Values {
4 String name() default "";
5 int value();
6 Class allowedTypes() default String.class,
7 ElementType[] types();
8 }
medium-2020-java-annotations-explained-07.java hosted with ❤ by GitHub view raw
Default values must be constant expressions, although null is not
acceptable. Arrays can return an empty array by using {} as their default
value.
. . .
Built-In Annotations
The JDK includes multiple annotations beside the ones we already
encountered for creating annotation types itself:
@Override
Indicates that a method overrides/replaces an inherited method. This
information is not strictly necessary, but it helps to reduce mistakes. If
we want to override a method but have a simple type in the signature,
or the wrong argument type, that error might go unnoticed. But if we
provide an @Override annotation, the compiler makes sure we
actually override a method, and not just accidentally add or overload
it.
@Deprecated
Another compile-only annotation. We can mark code as deprecated,
and the compiler/IDE can access this information to tell us the code
isn't supposed to be used anymore. Since Java 9, this previous marker
annotation becomes a configuration annotation. The values String
since() default "" and boolean forRemoval() default false were
added to provide even more info for compilers and IDE to work with.
@FunctionalInterface
Since Java 8, we can mark interfaces to be single abstract method
interfaces (SAM), so they can be used as lambdas. This marker
annotation allows the compiler to ensure that an interface has
precisely one single abstract method. If we add another abstract
method, our code will no longer compile. This annotation enables the
compiler check but isn’t strictly necessary. Any SAM is automatically a
functional interface.
@SafeVarargs
Another “trust me, I'm an engineer” marker annotation. Tells the
compiler that we won’t do any unsafe operation when using varargs.
@SuppressWarnings
A configuration annotation, accepting an array of warning names that
should be disabled during compilation.
. . .
How to Access Annotations at Runtime
Adding metadata isn’t enough. We also need to access it somehow.
Thanks to reflection, we can access it via the class-object:
Check for annotation
1 Class<MyAnnotatedType> clazz = anInstance.getClass();
2 // or: Class<MyAnnotatedType> clazz = MyAnnotatedType.class;
3
4 // Target: Package
5 boolean isTypeAnnotationPresent =
6 clazz.getPackage().isAnnotationPresent(MyAnnotation.class);
7
8 // Target: Type
9 boolean isTypeAnnotationPresent =
10 clazz.isAnnotationPresent(MyAnnotation.class);
11
12 // Target: Method
13 Method method = clazz.getMethod("myMethod");
14 boolean isMethodAnnotationPresent =
15 method.isAnnotationPresent(MyAnnotation.class);
16
17 // Target: Field
18 Field field = clazz.getField("myField");
19 boolean isFieldAnnotationPresent =
20 field.isAnnotationPresent(MyAnnotation.class);
medium-2020-java-annotations-explained-08.java hosted with ❤ by GitHub view raw
Access metadata
Equivalent to boolean isAnnotationPresent(Class<? extends Annotation>
annotationClass) , we also have methods for accessing the actual
annotation instance, providing us with access to its values.
Here are some of the methods available to different targets:
Classes
<A extends Annotation> A getAnnotation(Class<A> annotationClass) —
Returns a specific annotation, if present, otherwise null .
Annotation[] getAnnotations() — Returns all annotations on a given
type.
<A extends Annotation> A[] getAnnotationsByType(Class<A>
annotationClass) — Returns all annotations of a given annotation
type.
Methods
<T extends Annotation> T getAnnotation(Class<T> annotationClass) —
Returns a specific annotation, if present, otherwise null .
Annotation[] getDeclaredAnnotations() — Returns all annotations on
the method.
Annotation[][] getParameterAnnotations() — Returns an two-
dimensional array, containing the parameter annotations, in
declaration order.
. . .
Use Cases
An excellent use-case is serialization. With annotations, a lot of
additional metadata can be provided on how our data structures should
be processed.
Jackson, a JSON serialization framework, uses the @JsonProperty
annotation to provide every information necessary to modify the default
serialization process:
1 class SimplePojo implements Serializable {
2 private String name;
3
4 @JsonProperty(value = "json_name",
5 required = true,
6 access = ACCESS.READ_ONLY)
7 public String getName() {
8 return this.name;
9 }
10
11 public void setName(String name) {
12 this.name = name;
13 }
14 }
medium-2020-java-annotations-explained-09.java hosted with ❤ by GitHub view raw
Another excellent use-case is how RESTEasy uses annotations to
describe REST endpoints, so no additional config is needed elsewhere:
1 @Path("/1.0/login")
2 @Produces("application/json")
3 public class LoginResource {
4
5 @POST
6 @Path("/")
7 Response login(@FormParam("username") String username,
8 @FormParam("password") String passowrd,
9 @HeaderParam("User-Agent") String userAgent) {
10 ...
11 }
12
13 @HEAD
14 @Path("/")
15 Response ping() {
16 ...
17 }
18 }
medium-2020-java-annotations-explained-10.java hosted with ❤ by GitHub view raw
This way, RestEASY can perform routing ( @Path ), validates allowed HTTP
methods ( @POST and @HEAD ), provides data extracted from the request
( @FormParam and @HeaderParam ), and uses a defined media type for the
response ( @Produces ).
All without any additional config file or objects. The configuration is right
there in the corresponding code.
. . .
Conclusion
Annotations are a great way to provide additional data, either for
ourselves or third-party tools and libraries. But be aware of the
additional costs of parsing, compiling, and lookup of annotations,
especially at runtime.
. . .
Resources
The Java Tutorials — Annotations (Oracle)
java.lang.annotation package summary (JavaSE 8)
Creating a Custom Annotation (Baeldung)
T hanks to Felix Gonschorek and Zack Shapiro.
Programming Software Development Java Software Engineering Coding
225 claps
WRIT T EN BY
Ben Weidig Follow
Software developer, entrepeneur, blogger. Mostly Java,
sometimes Swift, Golang, Bash and all the other fun stu .
Better Programming Follow
Advice for programmers.
See responses (1)
More From Medium
Are Early Returns Any Simple Shell: What T he Chande Momentum Building an Image
Good? happens when you type Oscillator and In uxDB Downloader With
Daan in Better Programming ls -l? Anais Dotis in T he Startup Multiprocessing in
Emma Navarro in T he Startup Python
Amit Upreti in Better
Programming
Connecting graphical New to Ruby? Go Ahead How to Use Project Getting those pesky
objects to actions in and Pry! Lombok with Spring python modules to work
PyQt5 Dan Hutchinson Boot Isaac Adams
Wyatt Sharber, PhD Yiğitcan Nalcı in Javarevisited
Discover Medium Make Medium yours Become a member
Welcome to a place where words matter. On Medium, smart Follow all the topics you care about, and we’ll deliver the Get unlimited access to the best stories on Medium — and
voices and original ideas take center stage - with no ads in best stories for you to your homepage and inbox. Explore support writers while you’re at it. Just $5/month. Upgrade
sight. Watch
About Help Legal