articles

Home / DeveloperSection / Articles / Annotations in Java: Annotations at Runtime

Annotations in Java: Annotations at Runtime

Jonas Stuart 2375 28-May-2016

In the previous posts we learn the use of @Target and @Retention annotation. Now we understand this with the help of an example illustrated here.


The program presented here uses the introspection and reflection feature of the Java language. Let’s briefly discuss this feature so that we will not have any difficulties in understanding how annotations are discovered at runtime.

·   When the JVM loads a class in memory, it creates an object of the Class type for the loaded class. This object contains all the details about the class, which are available in its source program.

·   We obtain a reference to this Class object by calling the getClass method on an object loaded in memory.

·     We can introspect the various methods of the loaded class by calling the getMethods method on the Class object. The method returns an array of Method objects.

·    Method is a class that defines the physical representation of a method of a class. For example,

1- the getName method of the Method class returns its name;

2- the getParameterTypes method returns an array of Class objects that represent the formal parameter types;

3- the getReturnType method returns a Class object that represents the formal return type of the method.

In the program that follows, we use the getAnnotation method to obtain the annotation, if any, associated with the method. With this little introduction to introspection and reflection, we are now ready to learn the runtime discovery of annotations. You are encouraged to refer to the javadocs API for a full treatment of introspection and reflection.

Program Code
import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;

@WorkInProgress
public class RuntimeAnnotation {
                     @WorkInProgress
                     @Task(description = "Implement tax computations", estimatedHours = 50,                                          additionalNote = "This implementation is critical for the final launch")
                     public static float ComputeTax(float amount, float rate) {
                             return 0;
                     }
                     public static void main(String args[]) {
                             try {
                                      RuntimeAnnotation obj = new RuntimeAnnotation();
                                      Class cls = obj.getClass();
                                      WorkInProgress annotation = (WorkInProgress) cls
                                                          .getAnnotation(WorkInProgress.class);                                       System.out.println("Class " + cls.getName());
                                      if (cls.isAnnotationPresent(WorkInProgress.class)) {                                            System.out.println("\tThis class is not fully mplemented");                                       }
                                      System.out.println("\nList of methods:");                                       Method[] methods = cls.getMethods();
                                    for (Method method : methods) {
                                                System.out.println(method.getName());                                          if (method.isAnnotationPresent(WorkInProgress.class) {                                             System.out.println("\tThis method is not fully implemented");                                                 }
                                                if (method.isAnnotationPresent(Task.class)) {                                                           Task annotationTask = (Task) method                                                                              .getAnnotation(Task.class);                                                           System.out.printf("\tWhat TODO: "                                                           + annotationTask.description()                                                               + "%n\tTarget date: " +                                                                                     annotationTask.targetDate()                                                           + "%n\tEstimated hours: " +annotationTask.estimatedHours()
                                                                                + "%n\tNote: "
                                                          + annotationTask.additionalNote() + "%n");
                                                }
                                      }
                             } catch (Exception e) {
                                      System.out.print(e.getMessage());
                             }
                     }
}
@Retention(RetentionPolicy.RUNTIME)
@interface WorkInProgress {
}
@Retention(RetentionPolicy.RUNTIME)
@interface Task {
                     String description();
                     String targetDate() default "May 22, 2016";
                     int estimatedHours();
                     String additionalNote();

}

 Explanation

Like in earlier cases, we declare two annotations—WorkInProgress and Task—both having the same definitions as in the earlier examples. However, we apply a retention policy on both with the following statement:

@Retention(RetentionPolicy.RUNTIME)

Thus, these annotations are now available at runtime, which is what we want for this demonstration.

The main application class is RuntimeAnnotation. We apply the WorkInProgress annotation to it by preceding the class declaration with the @WorkInProgress annotation.

In the class definition, first we define the method ComputeTax, to which we apply the two annotations—WorkInProgress and Task. Next, we define the main method. We do not apply any annotations to it because we have fully implemented this method. In the main method, we create an instance of RuntimeAnnotation. Now comes the important part of introspection and displaying the annotation information at runtime. To do this, the program obtains the type of the created object by calling its getClass method:

Class cls = obj.getClass();

The program now obtains the associated annotation by calling the getAnnotation method on the obtained Class object:

WorkInProgress annotation = (WorkInProgress) cls.getAnnotation(WorkInProgress.class);

The getAnnotation method takes one parameter that specifies the annotation class type. Therefore, the method retrieves the annotation of the specified type, and if it is not found, null is returned. The program now retrieves the class name by calling its getName method for display to the user:

System.out.println("Class " + cls.getName());

Next, we check whether this element (the class) has an annotation present by calling the isAnnotationPresent method on the Class object; if it does, we print an appropriate message to the user:

if (cls.isAnnotationPresent(WorkInProgress.class)) {

System.out.println("\tThis class is not fully implemented");

}

We now introspect the class object cls to discover all the methods defined in it. This is done by calling the getMethods method on the Class object:

Method[] methods = cls.getMethods();

The method returns an array of Method objects. A Method is a class defined in the java.lang. reflect package and represents a method declaration in a class.

The method returns us the array of all the methods defined for the class. We iterate through this list by using a foreach loop:

for (Method method : methods) {

For each method, we print its name:

System.out.println(method.getName());

For each method, we check whether WorkInProgress annotation has been applied to it:

if (method.isAnnotationPresent(WorkInProgress.class)) {
System.out.println("\tThis method is not fully implemented");
}


Next, we check whether the Task annotation has been applied:

if (method.isAnnotationPresent(Task.class)) {

If so, we get the various members of the Task annotation and print their values to the console:

Task annotationTask = (Task) method.getAnnotation(Task.class);
System.out.printf("\tWhat TODO: "
+ annotationTask.description()
+ "%n\tTarget date: " + annotationTask.targetDate()
+ "%n\tEstimated hours: "
+ annotationTask.estimatedHours()
+ "%n\tNote: " + annotationTask.additionalNote()
+ "%n");

 Output

When we run the application, we see the following output:

Class RuntimeAnnotation
               This class is not fully implemented
List of methods:
main
ComputeTax
               This method is not fully implemented
                What TODO: Implement tax computations
                Target date: May 20, 2016
                Estimated hours: 50
                Note: This implementation is critical for the final launch
waitwait
wait
equals
toString
hashCode
getClass
notify
notifyAll

 

In the output, observe how the messages for the two annotations are printed and how the values of different members of the Task annotation are printed to the console.


Updated 31-Mar-2019

Leave Comment

Comments

Liked By