Custom Annotations

⭐ Interview Importance: MEDIUM
⏱️ Revision Time: 5 min

TL;DR

  • Custom Annotations allow you to define your own metadata tags using the @interface keyword.
  • By themselves, they do absolutely nothing. They are just passive markers.
  • They require a processor (usually written using Reflection) to scan for the annotation at runtime and execute corresponding logic.

Concept

To define an annotation, you must provide two essential Meta-Annotations:

  1. @Retention: Specifies how long the annotation is kept.
    • SOURCE: Thrown away by the compiler (e.g., @Override).
    • CLASS: Written to the .class file but ignored at runtime (Default).
    • RUNTIME: Loaded into JVM memory. Required if you want to read it via Reflection.
  2. @Target: Specifies where the annotation can be applied (e.g., METHOD, FIELD, TYPE for classes).

You can also define elements (properties) inside the annotation. They look like interface methods but act like configuration fields.

Examples

import java.lang.annotation.*;
import java.lang.reflect.Method;

// 1. DEFINE THE ANNOTATION
@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.METHOD)         
public @interface ExecuteTimes {
    int value() default 1; // 'value' is a special name. It allows @ExecuteTimes(5)
}

// 2. USE THE ANNOTATION
class TaskRunner {
    @ExecuteTimes(3)
    public void doWork() {
        System.out.println("Working...");
    }
}

// 3. PROCESS THE ANNOTATION VIA REFLECTION
public class AnnotationDemo {
    public static void main(String[] args) throws Exception {
        TaskRunner runner = new TaskRunner();
        Method method = TaskRunner.class.getMethod("doWork");
        
        // Check if the annotation is present
        if (method.isAnnotationPresent(ExecuteTimes.class)) {
            // Retrieve the annotation instance
            ExecuteTimes annotation = method.getAnnotation(ExecuteTimes.class);
            
            // Read the property and execute the logic
            int times = annotation.value();
            for (int i = 0; i < times; i++) {
                method.invoke(runner);
            }
        }
    }
}

Interview Questions

Q: What types can you use for an Annotation element?
A: Annotation elements are strictly limited. They can only return:

  1. Primitives (int, boolean, double, etc.)
  2. String
  3. Class
  4. An Enum
  5. Another Annotation
  6. A 1-dimensional array of any of the above.
    You cannot return a List<String> or an instance of a normal custom object (like User).

Q: What does the @Inherited meta-annotation do?
A: By default, if you put a custom annotation on a parent class, a child class extending it does not inherit that annotation. If you apply the @Inherited meta-annotation to your custom annotation definition, it tells the JVM that any class inheriting from the annotated class should also be considered to have the annotation. (Note: This only applies to class-level annotations, not methods or fields).