Complete Java Annotations Tutorial

An annotation is a kind of metadata in Java that can be applied to various elements of Java source code so that later some tool, debugger, or application program can take advantage of these annotations; and help analyze the program in a positive and constructive way. Just to mention, we can annotate classes, methods, variables, parameters, and packages in Java OR in one word almost everything. It is important to learn that the annotations applied on Java sourcecode are compiled into bytecode with other class members, and using reflection programmer can query this metadata information to decide the appropriate action to perform in any particular context.

In this tutorial, I am discussing all important concepts that you should keep handy with you, to make the best of this wonderful feature of Java language.

History and Overview of Java Annotations

In Java, Annotations were introduced as “A Metadata Facility” through JSR 175. The JSR description states its purpose as :

“A metadata facility for the Java-TM Programming Language would allow classes, interfaces, fields, and methods to be marked as having particular attributes”.

We are talking about metadata multiple times. What is this metadata in the Java language context? Why do we even care about them? Let’s understand the need for metadata with an example.

Below is a source code of class that is declared as final:

public final class MyFinalClass{
	//other class members
}

Now we have ‘final‘ keyword in the class declaration. And the impact of this declaration is that you can’t extend this class or make a child class of it. How compiler understand this? Simply because of ‘final‘ keyword. Right? Well, this is called metadata.

A metadata is data about data. Metadata adds some additional flags on your actual data (i.e. in above case the class MyFinalClass), and in runtime either you or JVM who understand these flags, can utilize this metadata information to make appropriate decisions based on context.

In Java, we use the annotations to denote metadata. We can annotate classes, interfaces, methods, parameters, and even packages. We have to utilize the metadata information represented by these annotations in runtime.

Built-in Annotations in Java

Obliviously you can define your own but Java does provide some in-built annotations too for ready-made use. In this section, we will learn about these in-build annotations and their detailed usages.

Before moving ahead, it’s important to remember that annotations are metadata and they can be applied to any part of the source code and even to other annotations as well. I will start by discussing annotations that should be applied to other annotations because it will make more sense when we start discussing annotations applicable to java source code.

Annotations Applied To Other Annotations

Generally below five annotations below are used inside other annotations to hint compiler how new annotation should be treated by JVM. Let’s explore these 5 annotations one by one.

@Retention

This annotation specifies how the marked annotation is stored in Java runtime. Whether it is limited to source code only, embedded into the generated class file, or it will be available at runtime through reflection as well.

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

//@Retention(RetentionPolicy.CLASS)
@Retention(RetentionPolicy.RUNTIME)
//@Retention(RetentionPolicy.SOURCE)
public @interface MyCustomAnnotation
{
	//some code
}

@Documented

This annotation indicates that new annotation should be included into java documents generated by java document generator tools.

import java.lang.annotation.Documented;

@Documented
public @interface MyCustomAnnotation {
   //Some other code
}

@Target

Use @Target annotation to restrict the usage of new annotation on certain java elements such as class, interface or methods. After specifying the targets, you will be able to use the new annotation on given elements only.

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target(value = {ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR,
                 ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.LOCAL_VARIABLE,
                 ElementType.PACKAGE, ElementType.PARAMETER})
public @interface MyCustomAnnotation {
   //Some other code
}

@Inherited

When you apply this annotation to any other annotation i.e. @MyCustomAnnotation; and @MyCustomAnnotation is applied of any class MyParentClass then @MyCustomAnnotation will be available to all child classes of MyParentClass as well. It essentially means that when you try to lookup the annotation @MyCustomAnnotation on any class X, then all the parent classes of X unto n level are queried for @MyCustomAnnotation; and if annotation is present at any level then result is true, else false.

Please note that by default annotations applied on parent class are not available to child classes.

import java.lang.annotation.Inherited;

@Inherited
public @interface MyCustomAnnotation {
   //Some other code
}

@Repeatable

By default, an annotation is applied on a java element only once. But, by any requirement, you have to apply a annotation more than once, then use @Repeatable annotation on your new annotation.

@Repeatable has been added in latest java 8 release.

@Repeatable(Schedules.class)
public @interface Schedule { ... }

Now use above annotation as below:

@Schedule(dayOfMonth="last")
@Schedule(dayOfWeek="Fri", hour="23")
public void doPeriodicCleanup() { ... }

Annotations Applied To Java Code

So far we learned about annotation which were meant to be applied on other annotations. Now we will look at other in-built annotations which are primarily targeted towards java sourcecode elements.

@Override

This annotation checks that the annotated method is overridden method. It causes a compile time “error” if the annotated method is not found in one of the parent classes or implemented interfaces. Very useful annotation and I will recommend to use it frequently.

public class DemoClass
{
   //some code

   @Override
   public String toString()
   {
      return super.toString();
   }

   @Override
   public int hashCode()
   {
      return super.hashCode();
   }
}

@Deprecated

Use this annotation on methods or classes which you need to mark as deprecated. Any class that will try to use this deprecated class or method, will get a compiler “warning“.

@Deprecated
public Integer myMethod()
{
	return null;
}

@SuppressWarnings

This annotation instructs the compiler to suppress the compile time warnings specified in the annotation parameters. e.g. to ignore the warnings of unused class attributes and methods use @SuppressWarnings("unused") either for a given attribute or at class level for all the unused attributes and unused methods.

@SuppressWarnings("unused")
public class DemoClass
{
     //@SuppressWarnings("unused")
     private String str = null;     

   //@SuppressWarnings("unused")
     private String getString(){
        return this.str;
     }
}

To see the list of all supported options to @SuppressWarnings, please refer to specific IDE reference documentation. e.g. for Eclipse refer to this complete list of values.

@SafeVarargs

Introduced in java 7, this annotation ensures that the body of the annotated method or constructor does not perform potentially unsafe operations on its varargs parameter. Applying this annotation to a method or constructor suppresses unchecked warnings about a non-reifiable variable arity (vararg) type and suppresses unchecked warnings about parameterized array creation at call sites.

public static <T> List<T> list( final T... items )
{
    return Arrays.asList( items );
}

@FunctionalInterface

This annotation is used to mark an interface as functional interface which are introduced in java 8. To read more about functional interfaces please follow the linked post.

@FunctionalInterface
public interface MyFirstFunctionalInterface {
	public void doSomeWork();
}

Custom Annotations in Java

All of the above annotations given examples above are in-built Java annotations and you can utilize them into your source code directly. Java allows you to create your own metadata in the form of custom annotations. You can create your own annotations for specific purposes and use them as well. Let’s learn how to create custom annotations.

Creating Custom Annotations

To create a custom annotation, you must use the keyword “@interface“. Other important things to remember while creating custom annotations are listed below:

  • Each method declaration defines an element of the annotation type.
  • Method declarations must not have any parameters or a throws clause.
  • Return types are restricted to primitives, String, Class, enums, annotations, and arrays of the preceding types.
  • Methods can have default values.

Some example custom annotation definitions and their usage can be listed as:

Example 1


// Declares the annotation DemoAnnotation without any value
public @interface DemoAnnotation {
}

//Use the annotation like below

@DemoAnnotation
public void toggle() {
}

Example 2

public @interface Author {
	String first();
	String last();
}

//Use the annotation like below

@Author(first = "Lokesh", last = "Gupta")
Book book = new Book();

Example 3

public @interface TravelRequest {
    int    id();
    String synopsis();
    String engineer() default "[unassigned]";
    String date()    default "[unimplemented]";
}

//Use the annotation like below

@TravelRequest(
    id       = 112233,
    synopsis = "Teleport me",
    engineer = "Mr. John Carter",
    date     = "04/01/3007"
)
public static void sendMeToMars () {
}

Using Custom Annotations

You must have got a brief idea of how annotations should be used in the above examples. Still, I am providing a more detailed example which we can later use in the next section where we will read the annotation values through reflection.

Based on the rules above listed, I have created one annotation, which has two attributes i.e. author and version. This can be applied on Java class, interface, enum OR any method only. Default values are provided to if it’s not there then also we print something.

package test.core.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface JavaFileInfo
{
   String author() default "unknown";
   String version() default "0.0";
}

Now to use the above annotation, all we have to do is annotate any class/interface of the method and provide the author name, and version of the file if any.

package test.core.annotations;

@JavaFileInfo
public class DemoClass
{
   @JavaFileInfo(author = "Lokesh", version = "1.0")
   public String getString()
   {
      return null;
   }
}

That’s all. It’s so easy to use annotations, right?

Processing Annotations Using Reflection

Till now, we have only created the annotation and then used it. The main reason we are using annotations are because they are metadata. So it means we should be able to fetch this metadata to utilize the annotation information when we need it.

In java, you have to use reflection API to access annotations on any type (i.e. class or interface) or methods. Let’s learn how to do this with an example.

package test.core.annotations;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;

public class ProcessAnnotationExample
{
   public static void main(String[] args) throws NoSuchMethodException, SecurityException
   {
      new DemoClass();
      Class<DemoClass> demoClassObj = DemoClass.class;
      readAnnotationOn(demoClassObj);
      Method method = demoClassObj.getMethod("getString", new Class[]{});
      readAnnotationOn(method);
   }

   static void readAnnotationOn(AnnotatedElement element)
   {
      try
      {
         System.out.println("\n Finding annotations on " + element.getClass().getName());
         Annotation[] annotations = element.getAnnotations();
         for (Annotation annotation : annotations)
         {
            if (annotation instanceof JavaFileInfo)
            {
               JavaFileInfo fileInfo = (JavaFileInfo) annotation;
               System.out.println("Author :" + fileInfo.author());
               System.out.println("Version :" + fileInfo.version());
            }
         }
      } catch (Exception e)
      {
         e.printStackTrace();
      }
   }
}

Output:

Finding annotations on java.lang.Class
Author :unknown
Version :0.0

Finding annotations on java.lang.reflect.Method
Author :Lokesh
Version :1.0

Summary

Before the advent of Annotations, you don’t need to define your source code metadata outside of some properties files. Now, they can directly define this meta-data information in the source code itself. If this feature wisely (as it is used in the latest Java frameworks like Spring and Struts), the benefits are countless.

Let’s summarize our learning from this post in some bullet points:

  1. Annotations are metadata that can be applied on either annotations OR other Java elements in Java source code.
  2. Annotations do not directly affect program semantics, but they do affect the way programs are treated by tools and libraries, which can in turn affect the semantics of the running program.
  3. Annotations can be read from source files, class files, or reflectively at run time.
  4. There are 10 in-built annotations as of today. 5 of them are meant to be applied on custom annotations and the other 5 are meant to be applied on Java source code elements. Read respective sections for more details.
  5. Because annotation types are compiled and stored in byte code files just like classes, the annotations returned by these methods can be queried just like any regular Java object. You saw an example above.

That’s all for this lovely and very powerful feature i.e. Annotation. Let me know of your thoughts/queries in the comments section.

Happy Learning !!

Comments

Subscribe
Notify of
guest
19 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode