How to create and implement custom annotations in Java?

java-annotationAnnotations are the most important feature of Java. There are lots more Java based frameworks available, which are working on annotations. For example JUnit, Hibernate, Spring etc. These few are most famous frameworks designed with the help of annotations. Therefore knowledge of annotations is quite necessary if you are working on Java.

“Annotation are tags or meta-data, which can be inserted in to source code. So that it could be process at runtime and can take decision according to that annotations.”

Java compiler understands couple of annotations, which we would discuss later in this article. But most important thing is to create own annotations or custom annotations. Lets see an example, how to create custom annotations in Java.

Annotation can be define with same way as we define interface but in case of annotation @interface keyword is used. Let us figure out some important points about custom annotation

1. @interface keyword is used to create annotation

2. Only public or default access modifier can be used for annotation.

3. Only public or abstract access modifier can be used for attributes of Annotation.

4. Only primitive types, String, Class, EnumAnnotation and one dimensional arrays of mentioned types are permitted as attributes in annotations.

5. Default value can be defined for attributes.

6. Four types of meta-annotation are used for custom annotation. Which are @Documented@Inherited, @Target and @Retention. These meta-annotations descriptions are mentioned below in this article.

package com.hawk.java;

import java.lang.annotation.Documented;
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;

/**
 * @author SoManyWord.com
 */
@Documented
@Inherited
@Target (ElementType.METHOD)
@Retention (RetentionPolicy.RUNTIME)
public @interface MethodMetaData {

	public String methodName();
	public int arguments() default 0;
	public Class<? extends Exception> expected() default java.lang.Exception.class;
	public String Author();

}

Code shown above is coding example of custom annotations. This is how you can create your own annotation in Java. Now its time to discuss more about meta-annotations used in custom annotation creation.

Meta-annotations of Annotations

There are four types of meta-annotations used to create custom annotations. According to javadoc these meta-annotations are describe as mentioned bellow:

@Documented: 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.

@Inherited: Indicates that an annotation type is automatically inherited. If an Inherited meta-annotation is present on an annotation type declaration, and the user queries the annotation type on a class declaration, and the class declaration has no annotation for this type, then the class’s superclass will automatically be queried for the annotation type. This process will be repeated until an annotation for this type is found, or the top of the class hierarchy (Object) is reached. If no superclass has an annotation for this type, then the query will indicate that the class in question has no such annotation.

Note that this meta-annotation type has no effect if the annotated type is used to annotate anything other than a class. Note also that this meta-annotation only causes annotations to be inherited from super classes; annotations on implemented interfaces have no effect.

@Target: Indicates the kinds of program element to which an annotation type is applicable. If a Target meta-annotation is not present on an annotation type declaration, the declared type may be used on any program element. If such a meta-annotation is present, the compiler will enforce the specified usage restriction. For example, this meta-annotation indicates that the declared type is itself a meta-annotation type. It can only be used on annotation type declarations rather then class, method or field. ElementType type enum is used to assign its values as mentioned in above example. For example @Target (value = ElementType.METHOD). ElementType enum has many more values available, use them accordingly.

@Retention: Indicates how long annotations with the annotated type are to be retained. If no Retention annotation is present on an annotation type declaration, the retention policy defaults to RetentionPolicy.CLASS.

For Example: RetentionPolicy.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.

In-Built Annotations in Java

Java has some in-built annotations, which are known for Java compiler. There are three types of in-built annotations in Java. Let us discuss something more about these in-built annotations. According to javadoc these in-built annotations are describe as mentioned bellow:

@Override: Indicates that a method declaration is intended to override a method declaration in a superclass. If a method is annotated with this annotation type but does not override a superclass method, compilers are required to generate an error message.

@Deprecated: A program element annotated @Deprecated is one that programmers are discouraged from using, typically because it is dangerous, or because a better alternative exists. Compilers warn when a deprecated program element is used or overridden in non-deprecated code.

@SuppressWarnings: Indicates that the named compiler warnings should be suppressed in the annotated element (and in all program elements contained in the annotated element). Note that the set of warnings suppressed in a given element is a superset of the warnings suppressed in all containing elements. For example, if you annotate a class to suppress one warning and annotate a method to suppress another, both warnings will be suppressed in the method.

As a matter of style, programmers should always use this annotation on the most deeply nested element where it is effective. If you want to suppress a warning in a particular method, you should annotate that method rather than its class.

Coding Example of Annotation Implementation in Java

This is time to create and use annotation in real life. Now we will see that how can we create an annotation? How to use annotation? How to use annotation at runtime and make decision on basis of annotation?

I hope most of you have used unit test framework JUnit, which use annotations to create test cases and execute them. If you didn’t use it, not a big reason to worry. Let us take an example, suppose we have a class called Maths, which have two methods sum(int a, int b) and divide(int a, int b) which support mathematics add and divide operations. Method divide(int a, int b) throws a custom exception BadParametersException, if any number is divided by zero, it is a invalid operation. In such case if method throws BadParametersException exception, means test case must be pass, because it is a valid case to throw an exception in such scenario. Therefore, I have created a @Test annotation with Expected attribute, which accept any kind to exception class, if method throws same exception, means test case is pass. This is the same way how this scenario is handled in JUnit. Let us try to resolve this case in our way.

Test.java (Annotation)

package com.hawk.java;

import java.lang.annotation.Documented;
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;

/**
 * @author SoManyWord.com
 */
@Documented
@Inherited
@Target (value = ElementType.METHOD)
@Retention (RetentionPolicy.RUNTIME)
public @interface Test {

	/**
	 * This field except <code>Class</code> type of any custom <code>Exception</code>.
	 * This annotation attribute is used whether method throw assigned custom <code>Exception</code>
	 * or not. If method throws custom <code>Exception</code>, it means test case is pass. 
	 * @return
	 */
	public Class<? extends Exception> Expected() default java.lang.Exception.class;

}

BadParametersException.java

package com.hawk.java;

/**
 * The class <code>BadParametersException</code> is sub-class of <code>Exception</code> class.
 * This class is used to throw exception if method arguments are invalid to process.
 * @author SoManyWord.com
 */
public class BadParametersException extends Exception {

	private String message = null;

	public BadParametersException() {
		super();
	}

	public BadParametersException(String message) {
		super(message);
		this.message = message;
	}

	public BadParametersException(Throwable throwable) {
		super(throwable);
	}

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

	@Override
	public String getMessage() {
		return this.message;
	}

}

Maths.java

package com.hawk.java;

/**
 * General math class, which support few mathematical operations.
 * @author SoManyWord.com
 */
public class Maths {

	public int sum(int a, int b) {
		return a + b;
	}

	public float divide(int a, int b) throws BadParametersException{
		if (b == 0) {
			throw new BadParametersException("Invalid patameter exception: No number can be divide by 0.");
		}
		float c = a/b;
		return c;
	}

}

UnitTests.java

package com.hawk.java;

/**
 * All test cases are written here with @Test annotation.
 * @author SoManyWord.com
 */
public class UnitTests {

	@Test(Expected = BadParametersException.class)
	public void testDivide() throws BadParametersException {
		Maths math = new Maths();
		math.divide(5, 0);
	}

	@Test
	public void testSum() {
		Maths math = new Maths();
		int sum = math.sum(10, 5);
		if (sum == 15)
			System.out.println("Sum Tast Case Pass, Output Value Is: " + sum);
	}

}

TestRunner.java

package com.hawk.java;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * This class is test suite runner. This class runs all test cases
 * provided in given test class. In main method test class is mentioned.
 * This class particularly check custom exception thrown by method. If thrown exception
 * match with expected exception mentioned on attributes of @Test annotation, that means
 * test case is pass. This same scenario is used in JUnit as well.
 * @author SoManyWord.com
 */
public class TestRunner {

	public void runUnitTests(String className) {
		try {
			Class<?> testClass = Class.forName(className);
			Object object = testClass.newInstance();
			Method[] methods = testClass.getMethods();
			for (Method method : methods) {
				if (method.isAnnotationPresent(com.hawk.java.Test.class)) {
					Test annotation = method.getAnnotation(com.hawk.java.Test.class);
					Class<? extends Exception> expectedClass = annotation.Expected();
					if (expectedClass != null) {
						try {
							method.invoke(object);
						}catch ( InvocationTargetException ex ) {
							if ( ex.getTargetException().getClass() == expectedClass ) {
								System.out.println("Test Case Pass With Custom Exception (BadParametersException): " + ex.getTargetException().getLocalizedMessage());
							} else {
								System.out.println("Test Case Fail With An Exception: " + ex.getMessage());
							}
						} catch ( Exception ex ) {
							System.out.println("Test Case Fail With An Exception: " + ex.getMessage());
						}
					}
				}
			}

		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		}

	}

	public static void main(String[] args) {

		TestRunner testRunner = new TestRunner();
		testRunner.runUnitTests("com.hawk.java.UnitTests");
	}

}

output


Test Case Pass With Custom Exception (BadParametersException): Invalid patameter exception: No number can be divide by 0.

Sum Tast Case Pass, Output Value Is: 15

One thought on “How to create and implement custom annotations in Java?

  1. Pingback: User Defined Marker Interface in Java | So Many Word

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>