Annotations have been around since Java 5, and nowadays, they are ubiquitous programming constructs that allow enriching the code. In this article, we’ll review some of the annotations questions that are often asked on technical interviews.

How can you create an annotation

Annotations are a form of an interface where the keyword interface is preceded by @and whose body contains annotation type element declarations that look very similar to methods:

public @interface SimpleAnnotation {
    String value();
    int[] types();
}

After the annotation is defined, yon can start using it in through your code:

@SimpleAnnotation(value = "an element", types = 1)
public class Element {
    @SimpleAnnotation(value = "an attribute", types = { 1, 2 })
    public Element nextElement;
}

Note that, when providing multiple values for array elements, you must enclose them in brackets.

Optionally, default values can be provided as long as they are constant expressions to the compiler:

public @interface SimpleAnnotation {
    String value() default "This is an element";
    int[] types() default { 1, 2, 3 };
}

Now, you can use the annotation without those elements:

@SimpleAnnotation
public class Element {
    // ...
}

or only some of them:

@SimpleAnnotation(value = "an attribute")
public Element nextElement;

What object types can be returned from an annotation method declaration

The return type must be a primitive, String, Class, Enum, or an array of one of the previous types. Otherwise, the compiler will throw an error.

Here’s an example code that successfully follows this principle:

enum Complexity {
    LOW, HIGH
}
public @interface ComplexAnnotation {
    Class<? extends Object> value();
    int[] types();
    Complexity complexity();
}

The next example will fail to compile since Object is not a valid return type:

public @interface FailingAnnotation {
    Object complexity();
}

Which program elements can be annotated

Annotations can be applied in several places throughout the source code. They can be applied to declarations of classes, constructors, and fields:

@SimpleAnnotation
public class Apply {
    @SimpleAnnotation
    private String aField;
    @SimpleAnnotation
    public Apply() {
        // ...
    }
}

Methods and their parameters:

@SimpleAnnotation
public void aMethod(@SimpleAnnotation String param) {
    // ...
}

Local variables, including a loop and resource variables:

@SimpleAnnotation
int i = 10;
for (@SimpleAnnotation int j = 0; j < i; j++) {
    // ...
}
try (@SimpleAnnotation FileWriter writer = getWriter()) {
    // ...
} catch (Exception ex) {
    // ...
}

Other annotation types:

@SimpleAnnotation
public @interface ComplexAnnotation {
    // ...
}

And even packages, through the package-info.java file:

@PackageAnnotation
package com.baeldung.interview.annotations;

Is there a way to limit the elements in which an annotation can be applied

The @Target annotation can be used for this purpose. If we try to use an annotation in a context where it is not applicable, the compiler will issue an error.

Here’s an example to limit the usage of the @SimpleAnnotation annotation to field declarations only:

@Target(ElementType.FIELD)
public @interface SimpleAnnotation {
    // ...
}

We can pass multiple constants if we want to make it applicable in more contexts:

@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PACKAGE })

We can even make an annotation so it cannot be used to annotate anything. This may come in handy when the declared types are intended solely for use as a member type in complex annotations:

@Target({})
public @interface NoTargetAnnotation {
    // ...
}

What are meta-annotations

Are annotations that apply to other annotations.

All annotations that aren’t marked with @Target, or are marked with it but include ANNOTATION_TYPE constant are also meta-annotations:

@Target(ElementType.ANNOTATION_TYPE)
public @interface SimpleAnnotation {
    // ...
}

What are repeating annotations

These are annotations that can be applied more than once to the same element declaration.

For compatibility reasons, since this feature was introduced in Java 8, repeating annotations are stored in a container annotation that is automatically generated by the Java compiler. For the compiler to do this, there are two steps to declared them.

First, we need to declare a repeatable annotation:

@Repeatable(Schedules.class)
public @interface Schedule {
    String time() default "morning";
}

Then, we define the containing annotation with a mandatory value element, and whose type must be an array of the repeatable annotation type:

public @interface Schedules {
    Schedule[] value();
}

Now, we can use @Schedule multiple times:

@Schedule
@Schedule(time = "afternoon")
@Schedule(time = "night")
void scheduledMethod() {
    // ...
}

How can you retrieve annotations? How does this relate to its retention policy

You can use the Reflection API or an annotation processor to retrieve annotations.

The @Retention annotation and its RetentionPolicy parameter affect how you can retrieve them. There are three constants in RetentionPolicy enum:

  • RetentionPolicy.SOURCE – makes the annotation to be discarded by the compiler but annotation processors can read them
  • RetentionPolicy.CLASS – indicates that the annotation is added to the class file but not accessible through reflection
  • RetentionPolicy.RUNTIME –Annotations are recorded in the class file by the compiler and retained by the JVM at runtime so that they can be read reflectively

Here’s an example code to create an annotation that can be read at runtime:

@Retention(RetentionPolicy.RUNTIME)
public @interface Description {
    String value();
}

Now, annotations can be retrieved through reflection:

Description description
  = AnnotatedClass.class.getAnnotation(Description.class);
System.out.println(description.value());

Will the following code compile

@Target({ ElementType.FIELD, ElementType.TYPE, ElementType.FIELD })
public @interface TestAnnotation {
    int[] value() default {};
}

No. It’s a compile-time error if the same enum constant appears more than once in an @Target annotation. Removing the duplicate constant will make the code to compile successfully.

Is it possible to extend annotations

No. Annotations always extend java.lang.annotation.Annotation. If we try to use the extends clause in an annotation declaration, we’ll get a compilation error:

public @interface AnAnnotation extends OtherAnnotation {
    // Compilation error
}

References

[1] Java Annotations Interview Questions: http://www.baeldung.com/java-annotations-interview-questions