Inherited Annotations in Java

Despite the fact that the fully operational annotation mechanism appeared in Java not so long ago, many developers have appreciated this new possibility of Java API and use it in their programs. Annotations have lots of advantages but have some restrictions too. One of them is the inability to inherit annotations to subclasses. This peculiarity brings in some inconvenience and could be critical if some wrappers, generated with the reflection mechanism, are used over some Java classes.

In this case, there is no possibility to manage annotations in derived classes manually and no ability to use this convenient tool of program behavior declarative description. To solve this problem, Fusionsoft Company developed the library of inherited annotations that is considered below. The library is open-source and free, with no restriction for commercial application.

Prerequisites

Java SDK 1.5 or higher;
junit-4.2 for test purposes.

Problem to Solve

Consider the following example:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface A {}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface B {}

@A
public interface BaseInterface {
   @B
   public void method1();
}

public class BaseClass {
   @B
   public void method2(){}
}

public class Derived extends BaseClass implements BaseInterface{
   public void method1(){}
   public void method2(){}
}

This example contains two kinds of annotations: the annotation @A for types, and the annotation @B for methods. The annotations were used to describe one superclass and one superinterface. A derived class inherited from the superclass and superinterface was created.

According to Java annotation practice, neither the derived class nor its methods inherit annotations defined in the superclass and superinterface. Therefore, the following code returns null in both cases:

Derived.class.getMethod(
   "method1", new Class[0]).getAnnotation(B.class)

Derived.class.getMethod(
   "method2", new Class[0]).getAnnotation(B.class)

Accessing Superclass Annotations

The main idea that allows getting annotations for superclasses lies in extracting them from annotated base classes by using the reflection mechanism. One could cache annotations in a unified storage area and get access to them by using the names of classes and signatures of methods as keys.

The product provides a centralized cache for annotated classes as follows:

public class AnnotationManager {
   private static Map<Class <?>, AnnotatedClass>
      classToAnnotatedMap =
      new HashMap<Class<?>, AnnotatedClass>();

   /**
    * @param theClass to wrap.
    * @return the annotated class wrapping the specified one.
    */
   public static AnnotatedClass getAnnotatedClass(Class<?> theClass){
      AnnotatedClass annotatedClass =
         classToAnnotatedMap.get(theClass);
      if (annotatedClass == null){
         annotatedClass = new AnnotatedClassImpl(theClass);
         classToAnnotatedMap.put(theClass, annotatedClass);
      }
      return annotatedClass;
   }
}

By calling the static method getAnnotatedClass, one can get the object of the class AnnotatedClass containing annotations inherited from ancestor classes and interfaces. AnnotattedClass uses the reflection methods: Class.getInterfaces and Class.getDeclaredAnnotations. It gets annotations recursively from all the ancestors, caches them, and associates annotations with the annotated interfaces and classes. The procedure of filling up the cache HashMap looks like the following:

private Map<Class<?>, Annotation> getAllAnnotationMapCalculated(){
   HashMap<Class<?>, Annotation> result = new
      HashMap<Class<?>, Annotation>();

   final Class<?> superClass = getTheClass().getSuperclass();
   // Get the superclass's annotations
   if (superClass != null)
      fillAnnotationsForOneClass(result, superClass);

   // Get the superinterfaces' annotations
   for (Class<?> c : getTheClass().getInterfaces())
      fillAnnotationsForOneClass(result, c);

   // Get its own annotations. They have preference to inherited
   // annotations.
   for (Annotation annotation : getTheClass().
      getDeclaredAnnotations())
      result.put(annotation.getClass().
      getInterfaces()[0], annotation);
   return result;
}

The lazy-initialization pattern is used here to cache annotations: Annotations are cached only when they have been requested for the first time.

Using the Library of Inherited Java Annotations

So, what does the access to Java annotations using the library of inherited annotations look like? The access to inherited annotations is identical to the standard one, but the special classes from the library are used instead of Java API classes.

For the example, it looks like the following:

AnnotatedClass annotatedClass = 
   AnnotationManager.getAnnotatedClass(Derived.class);
annotatedClass.getAnnotation(A.class);

AnnotatedMethod annotatedMethod = annotatedClass
   .getAnnotatedMethod("method1", new Class[0]);
annotatedMethod.getAnnotation(B.class);

annotatedMethod = annotatedClass
  .getAnnotatedMethod("method2", new Class[0]);
annotatedMethod.getAnnotation(B.class);

As long as method1 and method2 don't have parameters, you have used the empty array new Class[0] as a parameter of the getAnnotatedMethod function.

Conclusion

Using meta-information and declarative description of code behavior is very convenient and allows the creation of most reusable code. And, the possibility to inherit annotations is useful here. The library of inherited Java annotations provides this possibility. You can find the latest release of the library here: http://fusionsoft-online.com/download/static/products/annotation.zip. We welcome your feedback, opinions, and suggestions. Send your mail today to info@fusionsoft-online.com.



About the Author

Mikhail Milonov

Started writing programs 10 years ago, got qualified in various programming languages to date. As the moment widely uses Java and works in a fast growing software company in the field of semantic integration. PhD in Computer Science, Deputy director of Fusionsoft (http://www.fusionsoft-online.com)

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • "Security" is the number one issue holding business leaders back from the cloud. But does the reality match the perception? Keeping data close to home, on premises, makes business and IT leaders feel inherently more secure. But the truth is, cloud solutions can offer companies real, tangible security advantages. Before you assume that on-site is the only way to keep data safe, it's worth taking a comprehensive approach to evaluating risks. Doing so can lead to big benefits.

  • Live Event Date: August 20, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT When you look at natural user interfaces as a developer, it isn't just fun and games. There are some very serious, real-world usage models of how things can help make the world a better place – things like Intel® RealSense™ technology. Check out this upcoming eSeminar and join the panel of experts, both from inside and outside of Intel, as they discuss how natural user interfaces will likely be getting adopted in a wide variety …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds