25.6 Processing Annotations

It is possible to discover annotations on program elements at runtime using the Reflection API found in the java.lang.reflect package. As this topic is beyond the scope of this book, we provide a brief introduction to the Reflection API—in particular, how to discover annotations on a class and its members.

Figure 25.2 shows a few selected classes and interfaces from the Reflection API. At runtime, an instance of the class Class<T> represents the type T in a Java program. Classes, interfaces, enums, annotations, and primitive types are all represented by Class objects at runtime. For example, the class NuclearPlant is represented by a Class object which can be referenced by NuclearPlant.class and whose type is Class<NuclearPlant> at compile time to ensure type-safety before type erasure removes the type parameter.

Figure 25.2 Selected Types from the Reflection API

A Class<T> object representing a class can be queried for members that are declared in the class:

Click here to view code image

Constructor<?>[] cons = classobj.getDeclaredConstructors();
Method[] methods      = classobj.getDeclaredMethods();
Field[] fields        = classobj.getDeclaredFields();

The classes Class<T>, Method, Field, and Constructor<T> implement the java.lang.reflect.AnnotatedElement interface, as shown in Figure 25.2. The method getDeclaredAnnotations() of the AnnotatedElement interface can be used to obtain the annotations applied to an annotated element. All annotation types implicitly implement the java.lang.annotation.Annotation interface. The method getDeclared-Annotations() of the Annotation interface returns an array of Annotation containing the annotations applied to an annotated element. Using this method, we can obtain the annotations on a class or any member declared in the class.

Click here to view code image

Annotation[] annotations = annotatedElement.getDeclaredAnnotations();

If we are interested in a particular annotation on an annotated element, we can use the getDeclaredAnnotationsByType() method of the AnnotatedElement interface. The code below returns all @TaskInfo annotations applied on the NuclearPlant class. Since the @TaskInfo annotation is not repeatable, the array returned will have at most one TaskInfo object, which can be queried by calling the methods declared in the TaskInfo type declaration.

Click here to view code image

TaskInfo[] tias = NuclearPlant.class.getDeclaredAnnotationsByType(TaskInfo.class);

Of course, in order for an annotation to be discoverable at runtime, its retention policy must be RUNTIME.

In Example 25.4, the class NuclearPlant and its members are marked with annotations which have previously been declared in this chapter: @Pending and @TaskInfo. In addition, the standard annotations @Deprecated and @Override are also applied to some methods.

Example 25.4 Processing Annotations

Click here to view code image

@Pending
@TaskInfo(
    priority   = TaskInfo.TaskPriority.HIGH,
    taskDesc   = “Class for running a nuclear reactor.”,
    assignedTo = {“Tom”, “Dick”, “Harriet”}
    )
public class NuclearPlant {
  @Pending
  public NuclearPlant() {}
  @Deprecated(forRemoval = true, since = “8”)
  public boolean outOfProduction;
  @Deprecated(since = “10”)
  public void notInUse() {}
  @Pending
  @TaskInfo(
      taskDesc   = “Procedure for nuclear reactor shutdown”,
      assignedTo = {“Tom”, “Harriet”}
      )
  public void shutDownNuclearReactor() {}
  @TaskInfo(
      priority   = TaskInfo.TaskPriority.LOW,
      taskDesc   = “Exchange nuclear rods”,
      assignedTo = {“Tom”, “Dick”}
      )
  public void changeNuclearRods() {}
  @TaskInfo(
      priority   = TaskInfo.TaskPriority.LOW,
      taskDesc   = “Adjust nuclear fuel”,
      assignedTo = {“Harriet”}
      )
  public void adjustNuclearFuel() {}
  @TaskInfo(
      taskDesc   = “Start nuclear reactor”,
      assignedTo = “Dick”
      )
  public void startNuclearReactor() {}
  @Pending
  @Override
  public String toString() {
    return “TBD”;
  }
}

Click here to view code image

import static java.lang.System.out;

import java.lang.reflect.AnnotatedElement;
import java.util.Arrays;
import java.util.stream.Stream;

public class TaskInfoAnnotationProcessor {

  public static void printTaskInfoAnnotation(AnnotatedElement… elements) {// (1)
    Stream.of(elements)                                                     // (2)
      .filter(ae -> ae.isAnnotationPresent(TaskInfo.class))                 // (3)
      .peek(ae -> out.printf(“%s annotation for ‘%s’:%n”,                   // (4)
          TaskInfo.class.getName(), ae))
      .flatMap(ae -> Stream.of(
                       ae.getDeclaredAnnotationsByType(TaskInfo.class)))    // (5)
      .forEach(a -> {                                                       // (6)
        out.printf(”  Task description: %s%n”, a.taskDesc());
        out.printf(”  Priority: %s%n”, a.priority());
        out.printf(”  Assigned to: %s%n”, Arrays.toString(a.assignedTo()));
      });
  }
  public static void main(String[] args) {
    Class<?> classobj = NuclearPlant.class;                                 // (7)
    printTaskInfoAnnotation(classobj);                                      // (8)
    printTaskInfoAnnotation(classobj.getDeclaredMethods());                 // (9)
  }
}

Output from the program:

Click here to view code image

TaskInfo annotation for ‘class NuclearPlant’:
  Task description: Class for running a nuclear reactor.
  Priority: HIGH
  Assigned to: [Tom, Dick, Harriet]
TaskInfo annotation for ‘public void NuclearPlant.shutDownNuclearReactor()’:
  Task description: Procedure for nuclear reactor shutdown
  Priority: NORMAL
  Assigned to: [Tom, Harriet]
TaskInfo annotation for ‘public void NuclearPlant.changeNuclearRods()’:
  Task description: Exchange nuclear rods
  Priority: LOW
  Assigned to: [Tom, Dick]
TaskInfo annotation for ‘public void NuclearPlant.adjustNuclearFuel()’:
  Task description: Adjust nuclear fuel
  Priority: LOW
  Assigned to: [Harriet]
TaskInfo annotation for ‘public void NuclearPlant.startNuclearReactor()’:
  Task description: Start nuclear reactor
  Priority: NORMAL
  Assigned to: [Dick]

Leave a Reply

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