How well do you actually understand annotations in Java?
Annotations are everywhere in web or enterprise development. But how should they actually be defined? Adrian D. Finlay goes over the basics of Java annotations, their restrictions, retention policies, and more in this informative introduction to this coding mainstay.
If you are a web or enterprise developer, which most Java developers are, you consume annotations all the time. Whether in Spring, JEE, or Struts, you see them a lot. If you make use of unit testing tools like JUnit the same applies. If you do thick client or Android development, you may not see it as often in actual production code but rather your encounters with them will probably be with production tools, build tools, and testing. If you make use of Contexts & Dependency Injection then annotations are your mainstay.
Sure, you consume them. But do you know how to do basic things like how to define one? Maybe you do. But in my estimation, for the most part, if you are not developing APIs or deployment tools, you may rarely need to define an annotation. I think Annotations are the Java language element that is more commonly not fully understood. Maybe I’m wrong, but that’s just my opinion. Why don’t we start here.
Basic annotation definition
Notice the syntax of the annotation definition with
@interface denoting the type. This is how the compiler recognizes an annotation type. This syntax is because Annotation types are based of off the same plumbing behind the interface. Also notice the method declarations. You don’t implement these as you would do in a normal interface. The compiler does. Also, the
methods() are (for all concern to the applications developer) treated like fields when using them. Notice Ln. 8. We simply say age = 33 as if age were a ordinary member and not a method.
Although it is not obvious from the source code, the annotation used on Ln. 8 leaves the program unchanged. Recall that annotations are merely metadata, they do not change the nature of a program (at least not directly). Annotations, can signal to development tools to do a certain action based on the type of annotation. One good example of this is the CDI Engine of Java EE. What then happens as a consequence is that the CDI run-time provides X based on X annotation but the actual source file itself, still, does not change.
Restrictions on annotation types 
- Annotations cannot participate in inheritance.
- Annotations methods may not have arguments.
- Annotations cannot be generic or specify a throws clause.
- Annotations must return: an enum or primitive type or an annotation, String, or Class object. They can also return an array of these types.
The default annotations
The are several annotations used in Java code defined and used across the standard library and third party providers.
java.lang defined in the Java SE API , provides 6 standard annotations. By consequence of being included in
java.lang, they are automatically imported in every Java program. They are:
- @Deprecated — “A program element annotated
@Deprecatedis one that programmers are discouraged from using.” 
- @FunctionalInterface — “An informative annotation type used to indicate that an interface type declaration is intended to be a functional interface as defined by the Java Language Specification.” 
- @Override — “Indicates that a method declaration is intended to override a method declaration in a supertype.” 
- @SafeVarags — “A programmer assertion that the body of the annotated method or constructor does not perform potentially unsafe operations on its varargs parameter.” 
- @SupressWarnings — “Indicates that the named compiler warnings should be suppressed in the annotated element (and in all program elements contained in the annotated element).” 
java.lang.annotation as defined in the Java SE API , provides 6 standard annotations. They are:
- @Documented — “If the annotation
@Documentedis present on the declaration of an annotation type A, then any
@Aannotation on an element is considered part of the element’s public contract.”  Used to mark another annotation.
- @Inherited —“Indicates that an annotation type is automatically inherited.”  Used to mark another annotation.
- @Native — “Indicates that a field defining a constant value may be referenced from native code.” 
- @Repeatable — “The annotation type
java.lang.annotation.Repeatableis used to indicate that the annotation type whose declaration it (meta-)annotates is repeatable.”  Used to mark another annotation.
- @Retention — “Indicates how long annotations with the annotated type are to be retained.”  Used to mark another annotation.
- @Target — “Indicates the contexts in which an annotation type is applicable.”  Used to mark annother annotation.
I will not go into depth with these annotations. They are, however, the annotations that are commonly seen in standard Java SE development.
Single-member annotations, default values
Put simply, single-member annotations are annotations with only one member. That single member must be named
value(). Notice how the value is assigned using the parentheses on the annotation similar to a method argument.
Default values in action
Notice two things. The first is that because
occupation() has a default value, we need not specify a value for it when using the
@SampleAnnotation annotation, in the first example. In the second example, notice that because needn’t specify a value at all.
Retention policies are key concept to understand in using Annotations. There are three retention policies: CLASS, RUNTIME, & SOURCE.  They are defined in
java.lang.annotation.RetentionPolicy. Annotation retention policies specify how long an Annotation is to be retained. They are specified with the
@Retention annotation. The default retention policy is CLASS. They are, as follows:
- CLASS: “Annotations are to be recorded in the class file by the compiler but need not be retained by the VM at run time.” 
- RUNTIME: “Annotations are to be recorded in the class file by the compiler and retained by the VM at run time, so they may be read reflectively.” 
- SOURCE: “Annotations are to be discarded by the compiler.” 
Some readers may immediately notice that the ability for an annotation to persist until run-time allows for the use of Reflection to use the data. This is a key ability in the usefulness of annotations. We will briefly touch on this next. Also note that Annotations on local variable declarations (not initialized variables, just declarations) are not persisted in Java bytecode (a .class file).
Annotation information at runtime — Reflection
As we have just touched on, an annotations data may be obtained at runtime through the use of reflection. While we will not discuss the full extent of what you can do with this, I will provide a brief example demonstrating it’s use. Although we did not make use of it, the
AnnotatedElement interface provides some useful methods for performing reflection on Annotations at runtime. In the end, I did not directly use
Field(Ln. 7,8) from
What we did was:
- We made three annotations. SampleAnnotation & it’s Container Annotation called SampleAnnotationContainer (for repeated instance of SampleAnnotation)& SampleAnnotation2. We specified an array of values of type ElementType in the target of the SampleAnnotation as well as it’s container Annotation. We created RUNTIME annotations.
- We created a class called Annotate and we annotated the class as well as the class’s default constructor, main method, and a field in the class. Only one element was annotated with SampleAnnotation2 which was the
- Inside the
main()method we instantiated an object of the enclosing class to use for the basis of reflection and an declared an array of type Annotation to hold the annotations.
- We made five attempts to obtain annotations. The generic step for the first four were to instantiate an array of type Annotation with the annotations on that specific element as provided by the Class API.
- There are many ways to do it, but we did it as follows:
- Create an object of the class we are interested in.
Object.getClass()to get a Class<> object with which we can perform reflection.
- Call the
getField(), to get a more specific reference to the element with which we want to perform reflection. This step was skipped for the first attempt at reflection, which was on the class itself.
- Call the
Class.getAnnotations()method on the returned object to get the annotations of that object and assign that value to a variable of type Annotation .
- We handled exceptions, if necessary.
- We did some formatting and cycled through the array of type Annotation and printed the elements.
- We used a reference of our annotation type itself to perform reflection. This was a reference to
SampleAnnotation2. We instantiated an instance of
SampleAnnotation2using the same method described above except we called
getAnnotations(). To print the contents we called the method
id()of the Annotation. Because we did not specify a value to
id()when we used the annotation when we called our object of type
id()it returned the default value which is the String “Default ID #43216″.
Other things you can do with annotations
There are other things that one can do with annotations and while we will not discuss them in depth here they are worth mentioning so that you may discover them on your own.
As it has been alluded to with the presence of the
@Repeatable annotation, annotations can be repeated. For example:
The steps are essentially:
- Create the annotation to repeat.
- Mark the annotation to be repeated with
@Repeatable(x.class)where x is a container to hold the instances of the annotation that is to be repeated.
- Create the container for holding the instances of the repeatable annotation.
- Create and array of the annotation that is to be repeated and name the variable
value(). It must include a method specifically named
- Annotate to your hearts content.
getAnnotation()on the container annotation to get the instances of the annotation instead of calling
getAnnotation()on the original annotation itself.
As has been discussed earlier, as of JDK 8 one may annotate types. This notion is referred to as Type Annotations.
Type annotations must include “
@Target (ElementType.TYPE_USE)” in their definition so the compiler understands it as a Type Annotation. Type annotations are useful with the use of compiler plugins for source code tools, build tools, & development tools. I talk a lot more about this, including how to annotate a Method’s receiver (implicit this argument), below.
You can also create Marker Annotations. A marker annotation is simply an annotation that does not have any members.
@Override, for example, is a marker annotation. It simply let’s other users of your code know that you are overriding a superclass method. For example, when overriding
Object.toString() in classes you create.
There a number of other subtle features and rules of the API such as that you may not annotate a method return type of type void. I will leave it to you to do the discovery.
Knew about all this stuff already? Tell me about your experience in the comments below :)
SEE ALSO: Java EE 8: the baseline of Jakarta EE
 — Java: The Complete Reference, 9th Edition; Schildt, Herbert