Thursday, February 10, 2011

Custom annotations in Java

What is Annotation?

Simply speaking, annotation is a mechanism for associating a meta-tag with program elements and allowing the compiler or the VM to extract program behaviors from these annotated elements and generate interdependent codes when necessary.


Java annotations

There are many inbuilt annotations provided by Java like @Override,@Deprecated,@Documented,@SuppresWarnings and others.
In this section we will see how compiler makes use of @Override annotation and helps the developer in case they make some mistake while overriding


package com.kunaal.annotation;

/**
 * Example stating use of @Override annotation
 * @author Kunaal A Trehan
 */
public class OverrideExample {
 
 public static void main(String[] args) {
  SubClass subObj=new SubClass();
  subObj.printInfo1();
 }

}

/**
 * Base class having printInfo() method which will be overriden in subclass
 * @author Kunaal A Trehan
 */
class BaseClass{ 
 public void printInfo(){
  System.out.println("Base Class-print info");
 } 
}

/**
 * Subclass trying to over ride printInfo() method with wrong name.
 * It willbe acught at compilation time itself.
 * @author Kunaal A Trehan
 */
class SubClass extends BaseClass{
 @Override
 public void printInfo1(){
  System.out.println("Sub Class-print info");
 }
}

When we try to compile this program it gives an error that you have not actually over ridden the method.So it saves developer in figuring out why his/her overridden method is not getting called as his mistake will be pointed at compile time itself

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
 The method printInfo1() of type SubClass must override or implement a supertype method

 at com.kunaal.annotation.SubClass.printInfo1(OverrideExample.java:32)
 at com.kunaal.annotation.OverrideExample.main(OverrideExample.java:17)

Annotation infrastructure

To understand custom annotations we should understand following annotations which is applied on custom annotations.These are as follows:-
  • Retention
  • Target
  • Documented
  • Inherited
What is retention?
Retention states how JVM treats the custom annotation,for how long does the annotation retains.It can have following three values.These are:-
  • SOURCE- It means annotation will be discarded at the compile time.Class files won't have the annotation if the retention policy is defined as Source.
  • CLASS- It means annotation will be present in the generated class files.However these annotations won't be available at the run time.
  • RUNTIME- It means annotation is available to JVM at the run time.We can have custom logic to read those annotations and do something at the runtime.
What is target?
Target states where the annotation can be put.Whether we can put the annotation on field,method,class is defined by it.Possible values are as follows:-
  • TYPE - It means annotation can be applied to class or interface or enumeration
  • METHOD -It means annotation can be applied to method declaration only.
  • PARAMETER- It means annotation can be applied to parameter declaration only.
  • PACKAGE -It means annotation can be applied to package declaration only.
  • FIELD -It means annotation can be applied to field declaration only.
  • LOCAL_VARIABLE-It means annotation can be applied to local variable declaration only.
  • CONSTRUCTOR--It means annotation can be applied to constructor only.
  • ANNOTATION_TYPE-It means annotation can be applied to annotation type only.
 What is documented?
 Java API excerpt states "Indicates that annotations with a type are to be documented by javadoc and similar tools by default. This type should be used to annotate the declarations of types whose annotations affect the use of annotated elements by their clients. If a type declaration is annotated with Documented, its annotations become part of the public API of the annotated elements."

What is Inherited?
By default annotations are not inherited.So if we want annotations to be inherited.Inherited annotation should be put on custom annotation.It works only with classes.

 
Example of custom annotation
Though this use case may not be good enough for practical scenario.But it will give you an idea on how to construct an annotation and use it in your application.
I am creating a custom annotation named @Audit which can be placed at method level and have retention policy of run time.Wherever this annotation is present it will generate the audit

Source Code for custom annotation -Audit.java
package com.kunaal.annotation;

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;

/**
 * Marker annotation stating that method shd be audited
 * This marker annotation is used by the calling code to do custom auditing 
 * as coded.
 * 
 * @author Kunaal A Trehan
 *
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Audit {
}



Source code for CurrencyConverter.java
package com.kunaal.annotation;

import java.lang.reflect.Method;

/**
 * Template design pattern incorporated in very crude way 
 * to show how we can use custom annotation in standalone
 * application.
 * 
 * Here depending upon whether the custom annotation is present or not
 * We do auditing
 *  
 * @author Kunaal A Trehan
 *
 */
public abstract class CurrencyConverter {

 private void preConversion(String baseCurr,String targetCurr,Double amt){
  Class<? extends CurrencyConverter> currClass = getClass();  
  Method[] methods = currClass.getDeclaredMethods();
  
  for(Method method:methods){
   boolean annotationPresent = method.isAnnotationPresent(Audit.class);
   
   if(annotationPresent){
    System.out.println(amt + baseCurr+ " is getting converted "+ targetCurr);
    break;
   }
  }  
 }
 
 private void postConversion(String baseCurr,String targetCurr,Double amt,Double afterConversion){
  Class<? extends CurrencyConverter> currClass = getClass();  
  Method[] methods = currClass.getDeclaredMethods();
  
  for(Method method:methods){
   boolean annotationPresent = method.isAnnotationPresent(Audit.class);
   
   if(annotationPresent){
    System.out.println(amt + baseCurr+ " got converted to "+ afterConversion +targetCurr);
    break;
   }
  }  

 }
 
 public void convert(String baseCurr,String targetCurr,Double amt){
  preConversion(baseCurr,targetCurr,amt);
  Double newVal = doConversion(amt);
  postConversion(baseCurr,targetCurr,amt,newVal);
 }
 
 protected abstract Double  doConversion(Double amt);
}

Source code for UKINRConverter.java
package com.kunaal.annotation;

/**
 * Currency converter subclass for UK->INR
 * with @Audit annotation for conversion method
 * 
 * @author Kunaal A Trehan
 *
 */
public class UKINRConverter extends CurrencyConverter {

 /**
  * Default constructor
  */
 public UKINRConverter() {
 }

 /**
  * Overridden method having logic to convert UK->INR
  */
 @Audit
 @Override
 protected Double doConversion(Double amt) {
  if(amt !=null)
   return new Double(amt.doubleValue()*65);
  else
   return null;
 }

}

Source code for USDINRConverter.java
package com.kunaal.annotation;

/**
 * Currency converter subclass for USD->INR
 * with @Audit annotation for conversion method
 * 
 * @author Kunaal A Trehan
 *
 */
public class USDINRConverter extends CurrencyConverter {

 /**
  * Default constructor
  */
 public USDINRConverter() {
 }

 /**
  * Overridden method having logic to convert USD->INR
  */
 @Audit
 @Override
 protected Double doConversion(Double amt) {
  if(amt !=null)
   return new Double(amt.doubleValue()*46);
  else
   return null;
  
 }
}

Source code for NAINRConverter.java
package com.kunaal.annotation;

/**
 * Subclass of currency converter with implementation yet to be plugged in 
 * 
 * @author Kunaal A Trehan
 *
 */
public class NAINRConverter extends CurrencyConverter {

 /**
  * Default constructor
  */
 public NAINRConverter() {
 }

 /**
  * Overridden method without @Audit annotation and no implementation
  */
 @Override
 protected Double doConversion(Double amt) {
  return new Double(0);
 }

}

Source code for CustomAnnExample.java where we use above mentioned converters.Here Audit message is generated for USDINRConverter and UKINRConverter
package com.kunaal.annotation;

/**
 * In this class we are creating objects of USD-INR converter,
 * UK-INR converter,NA-INR converter and invoking convert method on it
 * 
 * NA-INR converter is blank having no implementation and 
 * without custom @Audit annotation .So auditing won't happen 
 * On other auditing happens.
 * 
 * @author Kunaal A Trehan
 *
 */
public class CustomAnnExample {

 public static void main(String[] args) {
  CurrencyConverter usdConverter=new USDINRConverter();
  CurrencyConverter ukConverter=new UKINRConverter();
  CurrencyConverter naConverter=new NAINRConverter();
  
  usdConverter.convert("USDollar", "INR", new Double(1000));
  System.out.println("*****************************************");
  ukConverter.convert("UKPound", "INR", new Double(1000));
  System.out.println("*****************************************");
  naConverter.convert("NA", "INR", new Double(1000));
  System.out.println("*****************************************");
 }

}

Corresponding output on runing the code is as follows.It clearly shows that Audit code is not run for NAINRConverter
1000.0USDollar is getting converted INR
1000.0USDollar got converted to 46000.0INR
*****************************************
1000.0UKPound is getting converted INR
1000.0UKPound got converted to 65000.0INR
*****************************************
*****************************************

Example for @Inherited annotation on custom annotation
Here I have created two annotations @Description and @InheritedDesc
Only in @InheritedDesc we have meta tag @Inherited.
Then we have applied both the annotations to SuperClass.
SubClass extends from SuperClass and in our main method we are printing
annotations available on each class.

package com.kunaal.annotation.inheritance;

import java.lang.annotation.Annotation;

/**
 * Example stating the difference of @Inherited in custom annotation
 * Here we are printing the annotations which exist at superclass
 * and extended subclass.
 * 
 * @author Kunaal A Trehan
 */
public class InheritanceAnnExample {
 
 public static void main(String[] args) {
  Annotation[] annotations = SuperClass.class.getAnnotations();
  
  System.out.println("Annotations available on superclass are as follows:-");
  for(Annotation ann:annotations){
   System.out.println(ann.annotationType().getCanonicalName());
  }
  
  System.out.println("=====================================================================");
  
  annotations = SubClass.class.getAnnotations();
  System.out.println("Annotations available on subclass are as follows:-");
  for(Annotation ann:annotations){
   System.out.println(ann.annotationType().getCanonicalName());
  }  
 }
}

/**
 * Superclass having two annotations 
 * -One with inherited attribute on [InheritedDesc]
 * -One without inherited attribute[Description]
 * 
 * @author Kunaal A Trehan
 */
@Description(data="SuperClass implementation")
@InheritedDesc(data="Testing inheritance among annotations")
class SuperClass{
 void print(){
  System.out.println("Super class implementation");
 }
}

/**
 * Subclass extending the superclass with no annotations 
 * explicitly added
 * 
 * @author Kunaal A Trehan
 */
class SubClass extends SuperClass{
 void print(){
  System.out.println("Sub class implementation");
 }
}

Output of the above example is as follows:-
Annotations available on superclass are as follows:-
com.kunaal.annotation.inheritance.InheritedDesc
com.kunaal.annotation.inheritance.Description
=====================================================================
Annotations available on subclass are as follows:-
com.kunaal.annotation.inheritance.InheritedDesc



Rules for annotation
  • Annotation declaration should start with an ‘at’ sign like @, following with an interface keyword, following with the annotation name.
  •  Method declarations inside the annotation source code should not have any parameters.
  •  Method declarations inside the annotation source code should not have any throws clauses.
  • Return types of the method should be one of the primitives,String,Class,enum,array of the primitives,String ,Class and enum

Advantages of annotation

  • Its widely used as replacement for XML files in frameworks like Spring,Hibernate and others.
  • Moreover its used in places which does not fall under some particular hierarchy.
    eg.
    Lets take the case you want to do some logging.One way is implement logging in every class
    and mix the business logic along with logging infrastructure.
    However we can define a custom annotation over there which could be used by the class reader or runtime environment to generate proxy over your business method implementation and plug the logging.
    So developer only puts the custom annotation,rest of magic is taken care by annotation handler.
  • Custom annotations is widely used in Junit4[@Test,].By annotating method with @Test,it states that it is a test case which in previous versions needs to be something like this [public void test XXXXX()]
  • Marker annotation is used by various frameworks to do compile time checking like @Override or some other custom logic.



21 comments:

  1. Thanks! This is really nice post…Here is an article available on same topic, if someone interested can read from here..

    http://www.somanyword.com/2014/02/how-to-create-and-implement-custom-annotations-in-java/

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete
  3. In the Java computer programming language, an annotation is a form of syntactic metadata that can be added to Java source code. Classes, methods, variables, parameters and packages may be annotated.
    Thanks a lot! You made a new blog entry to answer my question; I really appreciate your time and effort.
    java training institutes in chennai |
    java j2ee training institutes in velachery

    ReplyDelete
  4. Thanks for sharing this blog. This very important and informative blog for JAVA.PYTHON,JULIA,SQL etc
    I really loved reading this. Keep it up.!!
    Learned a lot of new things from your post! Good creation and HATS OFF to the creativity of your mind.
    Very interesting and useful blog!
    Looking forward to your response!
    best Java training in Gurgaon


    Thanks,
    Mahesh Chandan

    ReplyDelete
  5. This idea is mind blowing. I think everyone should know such information like you have described on this post. Thank you for sharing this explanation.
    Read more about java training delhi, java development training

    ReplyDelete
  6. Nice Information.
    If anyone who wants java training in Noida, so i suggest you please join KVCH Java classes.
    Best Java Training Course in Noida

    Advance Java Certification Training in Noida

    ReplyDelete
  7. Thanks for sharing this unique information with us. Your post is really awesome. Your blog is really helpful for me..
    Top college in Jaipur

    ReplyDelete
  8. Cool you make, the information is truly salubrious further shocking, I'll give you an interface with my scene. https://vograce.com

    ReplyDelete
  9. Thank you for the Post and it was good. Digital Marketing Course in Gurgaon you can learn more about digital marketing.

    ReplyDelete
  10. Thanks for sharing wonderful and informative piece of content. Keep on posting.
    Read my blogs : Concurrency and Synchronization in Java: Mastering Multithreading

    ReplyDelete