Complete Java Annotations Tutorial

An annotation is a kind of meta data in java which can be applied at various elements of java sourcecode so that later some tool, debugger or application program can take advantage of these annotations; and help analyzing the program in 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 is compiled into bytecode with other class members, and using reflection programmer can query this meta data information to decide the appropriate action to perform in any particular context.

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

Discussion Points

History and Overview of Java Annotations
Built_in Annotations in Java
	Annotations Applied To Other Annotations
		@Retention
		@Documented	
		@Target
		@Inherited
		@Repeatable
	Annotations Applied To Java Code
		@Override
		@Deprecated
		@SuppressWarnings
		@SafeVarargs
		@FunctionalInterface
Custom Annotations in Java
	Creating Custom Annotations
	Using Custom Annotations
Processing Annotations Using Reflection
Summary

History and Overview of Java Annotations

In java, Annotations were introduced as “A Metadata Facility” through JSR 175. The JSR description states it’s 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 meta data multiple times. What is this metadata in java language context? Why we even care about them? Let’s understand the need to metadata with an example.

Below is a sourcecode of class which is declared as final:

public final class MyFinalClass{
	//other class members
}

Now we have ‘final’ keyword in 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, interface, methods, parameters and even packages also. We have to utilize the metadata information represented by these annotations in runtime usually.

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 remind that annotations are metadata and they can be applied to any part of sourcecode and even on other annotations as well. I will start by discussing annotations which should be applied on other annotations because it will make more sense when we start discussing annotations applicable on java sourcecode.

Annotations Applied To Other Annotations

Generally below discussed five annotations are used inside other annotations to hint compiler that 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 annotation given examples above are in-built java annotations and you can utilize them into your sourcecode directly. Java allows you to create your own metadata in form of custom annotations. You can create your own annotations for specific purposes and use them as well. Let’s learn how to do 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 annotaion 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 above examples. Still, I am providing a more detailed example which we can later use in next section where we will read the annotation values through reflection.

Based on rules above listed, I have created one annotation @JavaFileInfo, 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 above annotation, all we have to do is to annotate any class/interface of method and provide the author name, and version of 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 sourcecode metadata outside in some properties file. Now, they can directly define this meta-data information in the source code itself. If used this feature wisely (as it is used in latest java frameworks like Spring and Struts), benefits are countless.

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

  1. Annotations are metadata which can be applied on either annotations OR other java element in java sourcecode.
  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 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 comments section.

Happy Learning !!

Was this post helpful?

Join 7000+ Fellow Programmers

Subscribe to get new post notifications, industry updates, best practices, and much more. Directly into your inbox, for free.

20 thoughts on “Complete Java Annotations Tutorial”

  1. could you please explain , how jvm finds out the implementation of Custom Annotation ?? , In Actual , If we are providing the Processing Methods or not , then , how jvm behaves.

  2. This is really very nice post about annotation in java.
    you explained very well with examples, very nice and clear.
    thank you so much for this useful post
    please keep posting for us

  3. Hi Lokesh, in the sample code you have given, you already knew which all classes might have your annotation applied (only DemoClass in this example). How could it be used when we want to provide this code as library and don’t know which classes a client project might have?

  4. You missed one important point at @target annotation definition. That is If we do not define any target type that means annotation can be applied to any element.

  5. Hi Lokesh,

    First time am reading your blog and enjoyed your annotations article. Simple explanation of complex topic.

  6. if I have to use your @JavaFileInfo annotaion in my code on 100 classes then I have to process all 100 classes , then whts the fun in annotaion?

  7. Very nice tutorial as usual ……thanks for it…but how to process the class or source level annotations ?

  8. pretty nicely explained this wonderful feature Annotation topic. I need small information . normally when you write Jersy REST services we use @Path annotation at the class level . this path should be unique . if we define same path twice jersey framework catches this and throws exception . question is : is there any way to catch that unique url at compile time?

    sitaram Venkata

    • At compile time..?? Compilation is very complex process and JVM does a lot of things; but nowhere it validates business rules for any application of framework. So, I believe that it’s impossible to validate the duplicate path annotations at compile time. You can detect them only in runtime, when class loaders have loaded the bytecode in memory for execution.

  9. As i asked you before about reflection concept and requested about its practical importance but u didn’t reply,,,plse sir do complete operation of reflection.

Comments are closed.

HowToDoInJava

A blog about Java and its related technologies, the best practices, algorithms, interview questions, scripting languages, and Python.