DEV Community

Cover image for Java Annotations
Patricia Nicole Opetina
Patricia Nicole Opetina

Posted on

Java Annotations

πŸ“Œ Annotation Basics

Annotations, a form of metadata, provide data about a program that is not part of the program itself. It has no direct effect on the operation of the code they annotate.

Annotations hava a number of uses

  1. Information for the compiler. Annotations can be used by the compiler to detect errors or supress warnings.
  2. Compile-time and deployment time processing. Software tools can process annotation information to generate code, XML files and so forth.
  3. Runtime processing. Some annotations are available to be examined at runtime.

πŸ“Œ Declaring an Annotation Type

The simplest format of an annotation looks like

@Entity
Enter fullscreen mode Exit fullscreen mode

The @ (sign character) indicates to the compiler that what follows is an annotation. The annotation above has a name Entity.

Annotations can also include elements which can be named or unnamed. For instance,

@Author(
   name = "Benjamin Franklin",
   date = "3/27/2003"
)
Enter fullscreen mode Exit fullscreen mode

If there is only one element named value, then the name can be omitted, as in:

@SuppressWarnings("unchecked")
void myMethod() { ... }
Enter fullscreen mode Exit fullscreen mode

If the annotation has no elements, then the parentheses can be omitted,

@Override
void mySuperMethod() { ... }
Enter fullscreen mode Exit fullscreen mode

It is also possible to use multiple annotations on the same declaration.

@Author(name = "Jane Doe")
@EBook
class MyClass { ... }
Enter fullscreen mode Exit fullscreen mode

If the annotations have the same type, then that would be a repeating annotation.

@Author(name = "Jane Doe")
@Author(name = "John Smith")
class MyClass { ... }
Enter fullscreen mode Exit fullscreen mode

The annotation type can be one of the types that are defined in the java.lang or java.lang.annotation packages, like @Override and @SuppressWarnings, which are predefined Java annotations. It is also possible to define your own annotation type, i.e. custom annotations.

Where Can Annotation Be Used

Annotations can be applied to declarations: classes, fields, methods and other program elements.

As of Java 8, annotations can be applied to the use of types. This form of annotation is called a type annotation.

  • Class instance creation expression
new @Interned MyObject();
Enter fullscreen mode Exit fullscreen mode
  • Type Cast
myString = (@NonNull String) str;
Enter fullscreen mode Exit fullscreen mode
  • implements clause
class UnmodifiableList<T> implements
        @Readonly List<@Readonly T> { ... }
Enter fullscreen mode Exit fullscreen mode
  • Thrown exception declaration
void monitorTemperature() throws
        @Critical TemperatureException { ... }
Enter fullscreen mode Exit fullscreen mode

Defining the annotation type

The syntax for doing this is:

@interface ClassPreamble {
   String author();
   String date();
   int currentRevision() default 1;
   String lastModified() default "N/A";
   String lastModifiedBy() default "N/A";
   // Note use of array
   String[] reviewers();
}
Enter fullscreen mode Exit fullscreen mode

The annotation type definitions looks similar to an interface definition where the keyword interface is preceded by the @ sign. Annotations are a form of interface. The body of the annotation definition contains annotation type element declaration, which look a lot like methods. They can also be defined with default values filled in.

NOTE: To make the information of an annotation appear in Javadoc-generated documentation, it must be annotated with the @Documented annotation.

πŸ“Œ Predefined Annotation Types

A set of annotation types are predefined in the Java SE API.

Annotation Types Used by the Java Language

The predefined annotation types defined in java.lang are : @Deprecated, @Override, and @SuppressWarnings.

  • @Deprecated. This indicates that the marked element is deprecated and should no longer be used. The compuler generates a warning whenever a program uses a method, class, or field with this annotation. When an element is deprecated it should also be documented using the Javadoc @deprecated tag.
// Javadoc comment follows
    /**
     * @deprecated
     * explanation of why it was deprecated
     */
    @Deprecated
    static void deprecatedMethod() { }
Enter fullscreen mode Exit fullscreen mode
  • @Override. This informs the compiler that the element is meant to override an element declared in superclass. While it is not required to use this annotation when overriding a method, it helps to prevent errors. If a method marked with @Override fails to correclty override a method in one of its superclasses, the compiler generates an error.

  • @SupressWarnings. This tells the compiler to suppress specific warnings that it would otherwise generate. For instance,

// use a deprecated method and tell 
   // compiler not to generate a warning
   @SuppressWarnings("deprecation")
    void useDeprecatedMethod() {
        // deprecation warning
        // - suppressed
        objectOne.deprecatedMethod();
    }
Enter fullscreen mode Exit fullscreen mode

Every compiler warning belongs to a category. The Java language specification lists two categories: deprecation and unchecked. Unchecked warnings occur when interfacing with legacy code written before the advent of generics.

  • @SafeVarargs . When applied to a method or constructor, this asserts that the code does not perform potentially unsafe operations on its varargs parameter. When this annotation is used, unchecked warnings related to varargs are suppressed.
  • @FunctionalInterface. Introduced in Java 8, this indicates that the type declaration is intended to be a functional interface.

Annotations that Apply to Other Annotations

Annotations that apply to other annotations are called meta-annotations. There are several meta-annotations defined in java.lang.annotation.

  • @Retention. This specifies how the marked annotation is stored.
    • RetentionPolicy.SOURCE. The marked annotation is retained only in the source level and is ignored by the compiler.
    • RetentionPolicy.CLASS. The marked annotation is retained by the compiler at compile time, but is ignored by JVM.
    • RetentionPolicy.RUNTIME. The marked annotation is retained by the JVM so it can be used by the runtime environment.
  • @Documented. This indicates that whenever the specified annotation is used, those elements should be documented using the Javadoc tool (By default, annotations are not inclueded in the Javadoc).
  • @Target. This annotation marks another annotation to restrict what kind of Java elements the annotation can be applied to. A target annotation specifies one of the ff. element.

    • ElementType.ANNOTATION_TYPE. This can be applied to an annotation type.
    • ElementType.CONSTRUCTOR. This can be applied to a constructor.
    • ElementType.FIELD. This can be applied to a field or property.
    • ElementType.LOCAL_VARIABLE. This can be applied to a local variable.
    • ElementType.METHOD. This can be applied to a method level annotation.
    • ElementType.PACKAGE. This can be applied to a package declaration.
    • ElementType.PARAMETER. THis can be applied to the parameters of a method.
    • ElementType.TYPE. This can be applied to any element of a class.
  • @Inherited. This indicates that the annotation type can be inherited from the superclass (not true by default). When the user queries the annotation type and the class has no annotation for this type, the class' superclass is queried for the annotation type. This can only be applied to class declarations.

  • @Repeatable. Introduced in Java 8, this indicates that the marked annotation can be applied more than once to the same declaration or type use.

πŸ“Œ Type Annotations and Pluggable Type Systems

Before Java 8, annotations can only be applied to declarations. After Java 8's release, annotations can also be applied to any type use. This meant that annotations can be used anywhere where type is used., e.g. instance creation expressions (new), casts, implements clauses and throws clauses. This form of annotation is called type annotation.

Type Annotations were created to support improved analysis of Java programs way of ensuring stronger type checking. The Java SE 8 release does not provide a type checking framework, but it allows developers to write (or download) a type checking framework that is implemented as one or more pluggable modules that are used in conjunction with the compiler.

For instance, a custom plug-in check can be used to ensure that a certain variable is never assigned null.

@NonNull String str;
Enter fullscreen mode Exit fullscreen mode

When this code is compiled including the NonNull module, the compiler prints a warning if it detects a potential problem, allowing developers to modify the code to avoid the error. After the code is corrected, this particular error will not occur when the program runs.

With the judicious use of type annotations and presence of pluggable type checkers, the code can be stronger and less prone to error.

There are a lot of existing libraries for type checking modules. For example the Checker Framework created by University of Washington. This framework includes a NonNull module, as well as regular expression module and a mutex lock module. See this for more information.

πŸ“Œ Repeating Annotations

As of Java 8 release, repeating annotations are introduced which enables developers to use the same annotation to a declaration or type use.

Steps in Creating a Repeatable Annotation

  1. Declaring Repeating Annotations The annotation type must be marked with the @Repeatable metannotation. For instance
@Repeatable(Schedules.class)
public @interface Schedule {
  String dayOfMonth() default "first";
  // ...
}
Enter fullscreen mode Exit fullscreen mode

The value of the @Repeatable meta-annotation, in parentheses, is the type of the container annotation that the Java compiler generates to store repeating annotations. In this example, the containing annotation type is Schedules, so repeating @Schedule annotations is stored in an @Schedules annotation.

Applying the same annotation to a declaration without first declaring it to be repeatable results in a compile-time error.

  1. Declare the Containing Annotation Type The containing annotation type must have a value element with an array type. The component type of the array type must be the repeatable annotation type. The declaration for the Schedules containing annotation type is the following:
public @interface Schedules {
    Schedule[] value();
}
Enter fullscreen mode Exit fullscreen mode
Retrieving Annotations

There are several methods available in the Reflection API that can be used to retrieve annotations. The behavior of the methods that return a single annotation, such as AnnotatedElement.getAnnotation(Class<T>), are unchanged in that they only return a single annotation if one annotation of the requested type is present. If more than one annotation of the requested type is present, you can obtain them by first getting their container annotation. In this way, legacy code continues to work. Other methods were introduced in Java SE 8 that scan through the container annotation to return multiple annotations at once, such as AnnotatedElement.getAnnotationsByType(Class<T>).

Design Considerations

When designing an annotation type, the cardinality of annotations of that type must be considered. It is now possible to use an annotation zero times, once, or, if the annotation's type is marked as @Repeatable, more than once. It is also possible to restrict where an annotation type can be used by using the @Target meta-annotation. It is important to design annotation types carefully to ensure that the person using the annotation finds it to be as flexible and powerful as possible.

As always, cheers to lifelong learning!! 🍷

REFERENCES

  1. Java Documentation: Annotations

Top comments (0)