Code Generation using Annotation Processors in the Java language – part 1: Annotation Types

With this post I would like to start a series about Code Generation using Annotation Processors in the Java language, how powerful they are, and at the end show how to make use of them to generate source code on compile time.

During the series we will:

  • Introduce what are Annotations in the Java language.
  • Understand the common uses of Annotations and their scopes.
  • Understand what are Annotation Processors and their defined role.
  • Learn how to build Annotation Processors.
  • Learn how to run Annotation Processors from command line, Eclipse and Maven.
  • Learn how to use Annotation Processors to generate source code.
  • Learn how to use Annotation Processors to generate source code using an external template engine as Apache Velocity.

Code Generation using Annotation Processors in the Java language – part 1: Annotation Types

Annotations were first introduced in the Java language with the third edition of the Java Language Specification (1) and first implemented in Java 5.

Using annotations, we are able to add metadata information to our source code – build or deployment information, configuration properties, compilation behavior or quality checks.

Unlike Javadocs, annotations are strong typed, having that any annotation in use has a corresponding Annotation Type defined in the classpath. Besides that, annotations can be defined to be available at run-time – not possible with Javadocs.

Annotations syntax

Annotations always appears before the annotated piece of code and by convention, usually in its own line, indented at the same level.

Annotations may apply to packages, types (classes, interfaces, enums and annotation types), variables (class, instance and local variables – including that defined in a for or while loop), constructors, methods and parameters.

The simplest form of an annotation is without any element included, for example:

    @Override()
    public void theMethod() {…}

In this case, parentheses may be omitted:

    @Override
    public void theMethod() {…}

Annotations may include elements that are just name-value pairs separated by commas. Allowed types are primitives, strings, enums and arrays of them:

    @Author(name = "Albert",
            created = "17/09/2010",
            revision = 3,
            reviewers = {"George", "Fred"})
    public class SimpleAnnotationsTest {…}

When the annotation has only one element and its name is value, it can be omitted:

    @WorkProduct("WP00000182")
    @Complexity(ComplexityLevel.VERY_SIMPLE)
    public class SimpleAnnotationsTest {…}

Annotations may define default values for some or all of their elements. Elements with default values can be omitted from an annotation declaration.

For example, assuming the Annotation Type Author defines default values for revision (default is 1) and reviewers (default is an empty String array), the following two annotation declarations are equivalent:

    @Author(name = "Albert",
            created = "17/09/2010",
            revision = 1,
            reviewers = {})
    public class SimpleAnnotationsTest() {…}
    @Author(name = "Albert",        // defaults are revision 1
            created = "17/09/2010") // and no reviewers
    public class SimpleAnnotationsTest() {…}

Typical uses of annotations

There are three annotation types defined in the Java Language Specification – they are used by the Java compiler:

  • @Deprecated: Indicates that the marked element should not be used. The compiler will generate a warning whenever the marked element is used. It should be used alongside the Javadoc @deprecated, reserving the Javadoc to explain the motive for deprecating the element.
  • @Override: Indicates that the element is meant to override an element declared in a superclass. The compiler will generate a warning if it finds a marked element that it is not really overriding anything. Although it is not required it is useful to detect errors – for example if after creating the subclass someone else modifies the superclass method signature, we will be warned as soon as we rebuild the source code.
  • @SuppressWarnings: Indicates to the compiler that it should suppress some specific warning that the marked element would otherwise produce – for example to reduce compiler “noise” because of the use of deprecated API’s or unchecked generics operations when interacting with legacy, pre Java 5, code.

Since their introduction, many libraries and frameworks have incorporated annotations into their newer releases. With annotations used in place with source code, these libraries and frameworks have reduced, even removed, the needs for configuration files.

Brilliant examples can be seen in:

  • Java Enterprise Edition and its main components –  Enterprise JavaBeans, Java Persistence API or Web Services API’s.
  • Spring Framework – used thoroughly for configuration, dependency injection and inversion of control in the core framework and in other Spring projects.
  • Seam, Weld, Guice.
  • Apache Struts 2.

Annotation Types

Annotation Types are special interfaces in the Java language that define custom annotations.

An annotation type is defined using @interface instead of interface:

    public @interface Author {
        String name();
        String created();
        int revision() default 1;
        String[] reviewers() default {};
    }
    public @interface Complexity {
        ComplexityLevel value() default ComplexityLevel.MEDIUM;
    }
    public enum ComplexityLevel {
        VERY_SIMPLE, SIMPLE, MEDIUM, COMPLEX, VERY_COMPLEX;
    }

Annotation Types have some differences compared to regular interfaces:

  • Only primitives, strings, enums, class literals and arrays of them are allowed. Note that as Objects in general are not allowed, arrays of arrays are not allowed in Annotation Types (every array is an object).
  • The annotation elements are defined with a syntax very similar to that of methods, but keep in mind that modifiers and parameters are not allowed.
  • Default values are defined using the default keyword followed by the value that will be a literal, an array initializer or an enum value.

As in any other class or interface, an Enum Type can be nested in an Annotation Type definition.

    public @interface Complexity {
        public enum Level {
            VERY_SIMPLE, SIMPLE, MEDIUM, COMPLEX, VERY_COMPLEX;
        }
    …

Annotations used to define Annotations

The JDK comes with some annotations that are used to modify the behavior of the Annotation Types that we are defining:

  • @Documented: Indicates that the marked Annotation Type should be documented by Javadoc each time it is found in an annotated element.
  • @Inherited: Indicates that the marked Annotation Type is inherited by subclasses. This way, if the marked annotation is not present in a subclass it inherits the annotation in the superclass, if present. Only applies to class inheritance and not to interface implementations.
  • @Retention: Indicates how long the marked Annotation Type will be retained. Possible values are those of enum RetentionPolicy: CLASS (default – included in class files but not accessible at run-time), SOURCE (discarded by the compiler when the class file is created) and RUNTIME (available at run-time).
  • @Target: Indicates the element types to which the marked Annotation Type is applicable. Possible values are those of enum ElementType: ANNOTATION_TYPE, CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER and TYPE.

The series will continue on part 2: Annotation Processors. Read it here.

(1) ”The Java Language Specification, Third Edition” is available for free download here.
Update: new link to relevant JLS section in his new home at Oracle site here.

28 thoughts on “Code Generation using Annotation Processors in the Java language – part 1: Annotation Types

  1. In the article it is written that:
    @Override: Indicates that the element is meant to override an element declared in a superclass. The compiler will generate a warning if it finds a marked element that it is not really overriding anything.
    It is generated a compiler error, not just a warning.

    1. Actually warning/error flags are configurable and defaults may change depending on the JVM implementation and version. Which one are you using? When I wrote the article I was using Oracle’s JDK 6 32-bit.

  2. Jackson (Json library) uses classes as value, too:
    @JsonSubTypes({@Type(AbsoluteLayout.class), @Type(InLineLayout.class)})
    defined as:
    public @interface JsonSubTypes {
    public Type[] value();
    public @interface Type {
    public Class value();
    public String name() default “”;
    }
    }
    I don’t know if it is a recent addition to Java. It is useful as being a type safe reference to existing code.

      1. Ah, OK. Since it is not in your list of “primitives, strings, enums and arrays of them”, I thought they should be mentioned. Unless Class is seen as primitive, but I doubt so.

  3. I’m citing this blog in a research paper regarding product line architectures, annotations processing, and code generation. Thanks for sharing this work. One bit of criticism regarding your new Word Press layout – code snippets (specifically on this page) are less readable when viewed on a desktop due to (i assume) some mobile layout constraint.

  4. The series articles about java annotation is very usable for me.I hope you can let me translate them to Chinese for more Chinese to read them(I will point out your website,if anyone want to read the english version) 🙂

    1. Hello! That would be great to have a chinese translation! When you have it, please let us know where it is posted so I can cross-link from here. Thanks!

Leave a comment