Annotations - Java advanced (OCP)

Java provides built-in ready-to-use annotations as well as the possibility to create one’s own annotations. Annotations allow to add metadata which expresses explicit context and/or intent to various Java structures (target) as classes, methods, attributes…

This article is part of a series on advanced Java concepts, also corresponding to topics to study for upgrading from level I certification to level II certification (OCP level). The new Oracle Java certification framework (as from Java 11) only offers one certification covering both levels, so you’ll want to go through the level I (“OCA”) topics as well.

Annotations do not impact the behavior of their target. They are interpreted by dev tools, the environment, or the reader. For example, if you use the @Override annotation before a method that’s not (properly) overriding another, if using an IDE you’d see a compiler error, but other than that it doesn’t impact the behavior of the method.

The Java Core annotations are widely used in other tools or frameworks such as Java EE, JaxB, Spring framework…

Existing Java SE annotations

@Override is probably the best known. It checks that a method exists so that annotated method is actually overriding it.

@Deprecated raises a warning when annotated method is used, but doesn’t prevent from using it. In javadoc, it’s written @deprecated with lower case while the annotation name has a capital letter. It can take additional parameters:

@Deprecated(since="11", forRemoval=true)

@SuppressWarnings, used on class or method level, suppresses compiler warnings. It takes one or an array ({“…”,”…”}) of warning names to suppress. If the name(s) passed as parameter are not recognized, it won’t raise an error, but could output a warning (compiler dependent).

@FunctionalInterface validates the design of a functional interface. It creates a compile-time error!

@SafeVarargs suppresses warnings about unsafe operations on varargs parameter.

Creating custom annotations

Annotation is a special type of interface which can’t be implemented. Creating an annotation interface implicitly extends Annotation interface.

Interface can have elements (a value, whose declaration looks like a method declaration). An element can have a default value. In that case, the element is optional, the annotation can be written without specifying a value (parenthesis can be omitted or empty).

<public> @interface MyCustomAnnotation {
  boolean isSomething() default true; // an element
}

-----------------------------

// using the annotation:
@MyCustomAnnotation(isSomething = false)
public void anyMethod() { ...

// with default value:
@MyCustomAnnotation // defaults to true
public void anotherMethod() { ...

An element can be set without naming it explicitly if it’s named “value” and the annotation only sets that element (when used).


<public> @interface MyCustomAnnotation {
  boolean isSomething() default true;
  int value();
}

-----------------------------

@MyCustomAnnotation(5) // ok because the other element has a default value!
public void ...

@MyCustomAnnotation(value = 5, isSomething = false) // value must be named because it's not the only element

Elements can be primitives, String, Enum, Annotation, Class<?> or an array of one of those types. For annotation arrays, the annotations must be repeatable (see few paragraphs below).

Annotation availability

There are 3 retention policy values:

  • source: annotation is discarded by compiler
  • class: it’s discarded by runtime (default)
  • runtime: the annotation is accessible through reflection (used to make annotation available)

Retention is an annotation… on an annotation.

@Retention(RetentionPolicy.RUNTIME)
@interface MyCustomAnnotation {
  boolean isSomething(); // an element
}

Annotation target

By default, an annotation is applicable to all elements (class, method, field, local variable…). This can be changed using the Target annotation.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.CONSTRUCTOR)
@interface MyCustomAnnotation {
  boolean isSomething(); // an element
}

Possible element types for target are:

  • ANNOTATION_TYPE
  • CONSTRUCTOR
  • FIELD
  • LOCAL_VARIABLE
  • METHOD
  • MODULE
  • PACKAGE
  • PARAMETER
  • TYPE
  • TYPE_PARAMETER
  • TYPE_USE

Type can represent a class, interface or enum.

Repeatable annotations

Repeatable allows annotation to be used several times within a given container.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.CONSTRUCTOR)
@Repeatable(CustomCollection.class) // which container can hold repeated values of the annotation
@interface MyCustomAnnotation {
  boolean isSomething(); // an element
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface CustomCollection {
  MyCustomAnnotation [] annotations();
}

Annotation inheritance

Annotations are not inherited from a class to its subclasses, unless the annotation is marked as inherited.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.CONSTRUCTOR)
@Inherited
@interface MyCustomAnnotation {
  boolean isSomething(); // an element
}

Document annotations

Annotation is documented if explicitly defined. Otherwise, it won’t be included in javadoc.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.CONSTRUCTOR)
@Inherited
@Documented
@interface MyCustomAnnotation {
  boolean isSomething(); // an element
}

Access annotations through Reflection

Annotation [] as = MyClass.class.getAnnotations();
Class c = MyClass.class.getAnnotations()[0].getAnnotationType();
MyRepeatableAnnotation[] ras = MyClass.class.getAnnotationsByType(MyRepeatableAnnotation.class);
for (MyRepeatableAnnotation ra: ras) {
  System.out.println(ra.name() + " " + ra.age());
}

3 thoughts on “Annotations – Java advanced (OCP)

  1. Interesting topic, but I think you should give an example of usage of a custom annotation to show the real purpose :p

Leave a Reply

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

Skip to content