Make everything as simple as possible, but not simpler. -Albert Einstein 

Home Java SE How to Create Custom Annotations in Java

How to Create Custom Annotations in Java

Published on May 10, 2012 by in Java SE

Summary: This article highlights the steps involved in creating as well as processing custom annotations in Java.    Annotations are typically used to provide  additional information about the software.   This valuable information can be utilized at compile time, run time, or during the deployment phase.

Prerequisites: If you would like to obtain this article’s complete sample, it may be obtained from our GitHub repository.  All samples are Maven based java projects.

Let’s Get Started!  We will create a custom annotation  called @NotNullable.  This annotation will be applied to variables that cannot be null.  Next, we will demonstrate how to process the annotation at run time.  In order to create a custom Java annotation, perform the following steps:

1. Define an interface using the @ syntax.
2. Choose @Target type for the annotation.  In our example, we have chosen ElementType.FIELD.  The various options include the following:

@Target type Definition
ElementType.TYPE Applies only to Type. A Type can be a Java class or interface or an Enum or even an Annotation.
ElementType.FIELD Applies to Java fields
ElementType.METHOD Applies to Java methods
ElementType.PARAMETER Applies only to method parameters in a method definition
ElementType.FIELD Applies to Java fields
ElementType.CONSTRUCTOR Applies only to a constructor of a class
ElementType.LOCAL_VARIABLE Applies only to Local variables.
ElementType.ANNOTATION_TYPE Applies only to Annotation Types
ElementType.PACKAGE Applies only to packages

3. Choose @Retention type for the annotation. Retention Policy is a mechanism used to instruct the JVM to retain the Annotation one of three possible ways. In our example we have chosen @Retention.RUNTIME. The various options include the following:

@Retention policy Definition
RetensionPolicy.RUNTIME Annotation should be retained for runtime. Annotations of this type can be read reflectively.
RetensionPolicy.SOURCE  Annotations are to be discarded by the compiler.
RetensionPolicy.CLASS  Annotations will be included in the class file, but cannot be read reflectively

Listing 1:

package techbysample.javase.sample1.annotation;

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

 * @author

public @interface NotNullable {

    String message();

Process the Annotation: Next, let’s see how we can process our custom annotation.

In line 19, We access the object’s fields using reflection.  In lines 21-22, we check if the Field object contains an annotation of type NotNullable.  If so, in line 26 we get the value of the field.  If value is null, we store the field’s annotation message  in an error messages List object.

Listing 2:

package techbysample.javase.sample1.annotation.validator;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import techbysample.javase.sample1.annotation.NotNullable;

 * @author

public class NotNullableValidator {

    public static List validate(Object obj){
        List errorMessages = new ArrayList();
        Field[] fields = obj.getClass().getDeclaredFields();
        for( int i = 0; i < fields.length; i++ ){
            NotNullable notNullannotation = (NotNullable)fields[i].getAnnotation(NotNullable.class);
            if(notNullannotation != null ){
                    if(fields[i].get(obj) == null){
                }catch(Exception ex){
        return errorMessages;

Unit Test the Annotation: In order to test the NotNullable annotation, we utilize a JUnit test case. In line 25, we only set the value of firstname and purposely leave lastname null.

Listing 3:

package techbysample.javase.sample1;

import java.util.List;

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import techbysample.javase.sample1.annotation.validator.NotNullableValidator;
import techbysample.javase.sample1.model.Employee;

 * @author

public class NotNullTest {

	public void testEmployeeNotNull()
        Employee employee = new Employee();
        List errors = NotNullableValidator.validate(employee);
        Assert.assertEquals("Error(s): " + errors.toString(),0,errors.size());

From the ‘javase-sample1′ project directory, type:

mvn -Dtest=NotNullTest test

You should see the following output:

Running techbysample.javase.sample1.NotNullTest
Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.101 sec <<< FAILURE!

Results :
Failed tests:
testEmployeeNotNull(techbysample.javase.sample1.NotNullTest): Error(s):
[Employee lastName cannot be NULL!] expected: but was:

Tests run: 1, Failures: 1, Errors: 0, Skipped: 0

 Share on Facebook Share on Twitter Share on Reddit Share on LinkedIn
No Comments  comments 

Leave a Reply

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


©, all rights reserved.