Java Custom Annotations

My earlier post covered in-built annotations supported by Java 5. Today we will look at developing custom java annotations on our own.


Let’s begin by defining our own custom java annotation called Documentation. The definition of the annotation class is as below:

package com.vinraj.custom;

public @interface Documentation {

}

The only difference between an interface definition and that of an annotation is the presence of @ before the interface keyword. Now the annotation can have its own members.

package com.vinraj.custom;

public @interface Documentation {

    public String author();

    public String version();

    public String shortDescription();

    public String[] reviews();

}

Below are the general guidelines to be followed while defining annotations:

  1. Annotation declaration should start with an ‘at’ sign like @, following with an interface keyword, following with the annotation name.
  2. Method declarations should not have any parameters.
  3. Method declarations should not have any throws clauses.
  4. Return types of the method should be one of the following:
    • primitives
    • String
    • Class
    • enum
    • array of the above types

Let’s look at how the custom annotation Documentation can be used.

package com.vinraj.custom;

public class NewClass {

    @Documentation(
	author="James Smith",
	version="1.0",
	shortDescription="Testing",
	reviews={"good", "nice"})
    public void test() {

    }
}

The annotation member elements can be set to have default values. Here’s an example:

package com.vinraj.custom;

public @interface Documentation {

    public String author() default "Tim Burton";

    public String version() default "1.0";

    public String shortDescription();

    public String[] reviews();

}

Due to introduction of the default values the NewClass can be defined in the following manner:

package com.vinraj.custom;

public class NewClass {

    @Documentation(
	author="James Smith",
	shortDescription="Testing",
	reviews={"good", "nice"})
    public void test() {

    }

}

We have omitted the version member variable from the annotation Documentation in NewClass.

There are specific annotations which can only be used in the context of annotations. The annotations are target, retention, documented and inherited.

Let’s look at their usage one at a time. First let’s look at target annotation. The target annotation defines which program elements can have annotations of the defined type. The user can put in the following options:

  1. TYPE: Class, interface, or enum (not annotation)
  2. FIELD: member fields (including enum values)
  3. METHOD: methods (does not include constrcutors)
  4. PARAMETER: method parameter
  5. CONSTRUCTOR: constructor
  6. LOCAL_VARIABLE: local variable or catch clause
  7. ANNOTATION_TYPE: Annotation types
  8. PACKAGE: java package

Let’s change the Documentation annotation class to the following.

package com.vinraj.custom;

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

@Target(ElementType.METHOD)
public @interface Documentation {

    public String author() default "Tim Burton";

    public String version() default "1.0";

    public String shortDescription();

    public String[] reviews();

}

We have now limited the usage of the Documentation annotation to the class methods. Let’s change the implementation of the NewClass to the following:

package com.vinraj.custom;

public class NewClass {

    @Documentation(
	author="James Smith",
	shortDescription="Testing",
	reviews={"good", "nice"})
    public void test() {

    }

    @Documentation(
	author="James Smith",
	shortDescription="Testing",
	reviews={"good", "nice"})
    public String name = "";

}

A compilation error is raised for using Documentation annotation for class instance variable.

The retention annotation indicates where and how long annotations of this type will be retained. This annotation has three options source, class and runtime. If the retention option selected is source, the annotation is discarded post compilation. Examples of such annotation types are @Override and @SuppressWarnings. The next option is class, the annotation is discarded during class load. The last option is the runtime. The annotation information marked using this option is never discarded. The annotation should be available for reflection at runtime. Example of annotation that could be useful at runtime is @Deprecated.

The Documentation custom annotation can be added the Retention annotation in the following manner.

package com.vinraj.custom;

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

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Documentation {

    public String author() default "Tim Burton";

    public String version() default "1.0";

    public String shortDescription();

    public String[] reviews();

}

By default, the annotation and related information does not appear in the class javadoc. A marker annotation @Documented in provided to cause the annotation related information to be added in the class javadoc. The Documentation custom annotation can use the @DOcumented in the following manner.

package com.vinraj.custom;

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

@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Documentation {

    public String author() default "Tim Burton";

    public String version() default "1.0";

    public String shortDescription();

    public String[] reviews();

}

Now let’s move on to the last annotation @Inherited. Inherited is probably the most complex of all annotations and does provide an interesting option.

(Update: 24th Dec 09) This portion of this post content has been revised after Gustav pointed out an error in my earlier content.

To understand the @Inherited annotation let’s consider the following custom annotation CarAnnotation.

package com.test.annotations;

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

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface CarAnnotation {

    public String maker();

}

I create the following AbstractBMWCar with CarAnnotation annotation.

package com.test.annotations;

@CarAnnotation(maker="BMW")
public class AbstractBMWCar {

}

I also create a child class BMWRoadsterCar which inherits AbstractBMWCar. The source code is as follows:

package com.test.annotations;

public class BMWRoadsterCar extends AbstractBMWCar {

}

As I have defined the CarAnnotation with the @Inherited annotation, its informational content is available to the child class BMWRoadsterCar. As already clarified BMWRoadsterCar is a child class of AbstractBMWCar. To understand the implication, please run the following test class in two scenarios:

  • With the @Inherited annotation in the CarAnnotation.
  • Without the @Inherited annotation in the CarAnnotation (i.e. comment out line 11 of the CarAnnotation annotation class).

The test class source code is as below:

package com.test.annotations.test;

import java.lang.annotation.Annotation;

import com.test.annotations.AbstractBMWCar;
import com.test.annotations.BMWRoadsterCar;
import com.test.annotations.CarAnnotation;

public class CarAnnotationsTest {

	public static void main(String[] args) {
		
		Class[] classes = {AbstractBMWCar.class, BMWRoadsterCar.class};
		
		for (Class classObj : classes) {
			Annotation[] annotations = classObj.getAnnotations();
			System.out.println("No. of annotations: " + annotations.length);
			for (Annotation annotation : annotations) {
				CarAnnotation carAnnotation = (CarAnnotation)annotation; 
				System.out.println(carAnnotation.maker());
			}			
		}		
	}
}

The output for scenario 1 is :

No. of annotations: 1
BMW
No. of annotations: 1
BMW

The output for scenario 2 is :

No. of annotations: 1
BMW
No. of annotations: 0

Clearly, both the classes AbstractBMWCar and BMWRoadsterCar have access to CarAnnotation information when the CarAnnotation is defined with @Inherited; BMWRoadsterCar does not have access to the CarAnnotation when the @Inherited annotation is removed. Something to keep in mind when developing your own custom annotations.

That’s all from the annotation desk at the moment.

Advertisements

44 thoughts on “Java Custom Annotations

  1. Thanks, nice explanation!
    I’ve allready done those steps before reading this article, but what I don’t manage to do is, in another class, through Reflection, see if a field has the annotation that I’ve created. I’m using “getDeclaredAnottations” from a Field variable.
    Many thanks if you could help!
    P.S.- Yes, they are iin the same package.

  2. I’ve found the answer! I’m here to share it!
    You are able to use your own user defined annotations if you preceed them with “@Retention(RetentionPolicy.RUNTIME)” from “java.lang.annotation”. this allows you to consult the information in Runtime, because by default the retenion is set to CLASS.
    Have a nice weekend!

  3. You still have to create an abstract class to implement a default behaviour to an interface and extend from the abstract class.
    The @Inherited annotation will cause the annotation to be inherited to a subclass.
    If the annotation will not be processed by any tool the annotation will have no effect. So your description about the @Inherited annotation is wrong.

    1. Gustav,
      I made a slight change to the code. I removed the default “Vroom” portion of the code from the CarAnnotation makeNoise method and compiled the BMWCar class. I got the following error:
      annotation com.CarAnnotation is missing makeNoise @CarAnnotation.

      When I added the default “Vroom” code, the BMWCar.java compiled without any errors. So the implementation of abstract class is not mandatory. I know this is contrary to the javadoc mentioned for Inherited annotation.

      1. Mr. President,

        when you remove the default “Vroom” portion of the CarAnnotation you get a compile error because then you have to specify the makeNoise property every time you are using the @CarAnnotation. So it has to look like this:
        @CarAnnotation(makeNoise=”Vroom”).

        But even when you use the @CarAnnotation on the class BMWCar as you described – it does not implement the makeNoise method, i.e. you cannot call makeNoise() on a BMWCar object. In your first example where BMWCar extends the abstract class it is of course possible to call the makeNoise method. Annotations never change the sematic of your code unless a parser reads the annotaton and changes the behaviour of the code. Without a parser annotations have no effect on your code.

        Your example actually hasn’t anything todo with the @Inherited meta-annotation – if you remove it from your code it will still compile.

        A correct example of using the @Inherited meta-annotation would be to annotate the AbstractCar with the CarAnnotation. Then all subclasses of AbstractCar will inherit the CarAnnotation, i.e. you do not have to annotate BMWCar because it inherits the annotation from it’s super class.

      2. Gustav,
        I think I get your point. My bad; will fix the post soon. Thanks for your patience and inputs.

        Update:24th Dec 09, I have changed the blog content related to Inherited annotation.

  4. Tx for the good info. I’ll be using the Runtime annotations for decorating some fields I need to reflect on to get some associated meta data.

  5. Very nice content.
    I have a question on annotation:
    @Documentation(author=”James Smith”,
    version=”1.0″,
    shortDescription=”Testing”,
    reviews={“good”, “nice”})
    can we give value to author i.e. “James Smith” through a properties file?

    1. below works:
      static final String str = “James Smith”;
      @Documentation(author=str,
      version=”1.0″,
      shortDescription=”Testing”,
      reviews={“good”, “nice”})
      public void test() { }

  6. How come @Deprecated annotation is useful at runtime as you have mentioned in your above post. I think this is useful only at SourceTime. Plz clarify if I am wrong.

    1. Hi KK,

      One obvious use is knowing the deprecated methods of a class during runtime class interrogation.

      Some more details are available here.

  7. Very good explanation. The way you have explained is very beautiful. Appreciated it. But still, need more information about annotations.

  8. How to inherit the annotations specified to a method in a super class while overriding the same method in sub-class??
    Can u please gimme ur mobile number Mr.President. I have lot of doubts in Annotations. I cannot post all here.

  9. Mr President,
    Im planning to have a GD on annotaions in my company. So, need some more info annotations. Plz call me@8884921881. Waiting for ur call.

    1. Hi Santhosh Kumar,

      I will not be able to share my contact details. You can drop a comment and I can respond to them.

  10. Nice and helpful article. Do u have an article written on how we can process those custom Annotations !! Like a real use case scenario….

    1. Hi dyutiman,

      If you refer to my test class CarAnnotationsTest you will get an idea of how to gain annotation related information. To get an example of how to use annotation in real life use, look at how hibernate uses annotation for setting up configuration information.

  11. can i create an annotation where i can use the logger

    eg:

    @Logger

    class Test{

    LOGGER.info(“this is test method”);

    }

    means i want to just add @Logger above the class,it will use the respective class name and LOGGER instance.

    how can i do the same

    Regards

    sal

    1. Sal,

      The functionality you are looking to implement can be achieved using AOP(Aspect Oriented Programming). Look up Java AOP tools and how they can be used to achieve the same.

      Here are two links explaining how to implement AOP using Spring AOP.

      Hope this helps!

  12. Hi Mr. President

    I want to translate this post to different language which is Korean.

    In Korea java community, there is no enough information about java annotation.

    so, I want to share this post to the community as translated version.

    Could you let me do that?

    Thanks.

    1. Hi David,

      You are most welcome to translate my post in Korean. Drop me a line with the URL once done. Thanks for asking.

      Regards,
      Mr President

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s