0% found this document useful (0 votes)
87 views

Java Annotations Explained PDF

This document explains Java annotations through discussing their anatomy, basic usage, and key annotation types like @Retention and @Target. It describes that annotations allow adding metadata to code for various purposes. @Retention specifies an annotation's lifecycle availability while @Target restricts where an annotation can be used, such as on types, fields, methods, etc. Basic annotation usage involves placing @AnnotationName at compatible target sites, with optional values that can have defaults.

Uploaded by

Yaegar Wain
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
87 views

Java Annotations Explained PDF

This document explains Java annotations through discussing their anatomy, basic usage, and key annotation types like @Retention and @Target. It describes that annotations allow adding metadata to code for various purposes. @Retention specifies an annotation's lifecycle availability while @Target restricts where an annotation can be used, such as on types, fields, methods, etc. Basic annotation usage involves placing @AnnotationName at compatible target sites, with optional values that can have defaults.

Uploaded by

Yaegar Wain
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 1

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

You might also like