5.5 Java Annotations: Built-in, Meta-Annotations, and Custom Types

Key Takeaways

  • Java annotations attach declarative metadata to code elements without directly altering execution logic, supporting compiler checks, tool processing, and runtime reflection.
  • Standard compiler annotations include @Override, @Deprecated (with since and forRemoval attributes), @SuppressWarnings, @FunctionalInterface, and @SafeVarargs.
  • @SafeVarargs can only be applied to constructors, static methods, final instance methods, or private instance methods, and produces a compilation error on overridable instance methods.
  • Meta-annotations (@Target, @Retention, @Documented, @Inherited, @Repeatable) define annotation constraints, where @Retention defaults to CLASS if omitted.
Last updated: September 2026

5.5 Java Annotations: Built-in, Meta-Annotations, and Custom Types

Annotations provide a powerful, declarative mechanism to attach structured metadata to Java program elements (classes, methods, fields, parameters, modules, and type uses). On the 1Z0-830 exam, you will be tested on standard built-in compiler annotations, their strict placement rules, meta-annotations, and the syntax constraints governing custom @interface definitions.


Built-In Compiler Annotations

Java provides standard annotations in java.lang that instruct the compiler to perform checks, generate diagnostics, or suppress warnings.

1. @Override

Instructs the compiler to verify that the annotated method overrides an accessible method in a superclass or implements an abstract method from an interface. If the signature does not match exactly (e.g., parameter type mismatch or accidental overloading), compilation fails.

public class Account {
    // COMPILER ERROR: Parameter is Object, not Account -> does not override boolean equals(Object obj)
    // @Override
    // public boolean equals(Account other) { return false; }

    @Override
    public boolean equals(Object other) { return super.equals(other); } // OK
}

2. @Deprecated

Marks a program element as obsolete and discourages its use. Java 9 enhanced @Deprecated with two optional elements:

  • since: A String specifying the version in which the element was deprecated (e.g., since = "21").
  • forRemoval: A boolean indicating whether the element is scheduled for permanent deletion in a future release (default is false).
@Deprecated(since = "21", forRemoval = true)
public void legacyAuthenticate() {
    // Using this method generates a 'terminally deprecated' compiler warning
}

3. @SuppressWarnings

Instructs the compiler to suppress specific diagnostic warnings that would otherwise be emitted during compilation. Common values include "unchecked", "deprecation", "rawtypes", "removal", and "preview".

@SuppressWarnings({"unchecked", "deprecation"})
public void executeLegacyCode(List list) {
    list.add("data");
    legacyAuthenticate();
}

4. @FunctionalInterface

Enforces at compile time that an interface contains exactly one abstract method (SAM - Single Abstract Method), qualifying it for use with lambda expressions and method references. Default methods, static methods, and methods overriding java.lang.Object (e.g., boolean equals(Object)) do not count against the single abstract method constraint.

5. @SafeVarargs (High-Yield Exam Topic)

Suppresses unchecked warnings related to parameterized (generic) varargs arrays. Because generic array creation can cause heap pollution, the compiler issues warnings when combining generics with varargs.

[!CRITICAL] @SafeVarargs can ONLY be applied to methods that cannot be overridden:

  1. static methods
  2. final instance methods
  3. private instance methods (allowed since Java 9)
  4. Constructors

Applying @SafeVarargs to a public, protected, or package-private non-final instance method causes a COMPILATION ERROR.

public class SafeVarargsDemo {
    @SafeVarargs // OK: static method
    public static <T> void logAll(T... items) {}

    @SafeVarargs // OK: final instance method
    public final <T> void processFinal(T... items) {}

    @SafeVarargs // OK: private instance method (Java 9+)
    private <T> void processPrivate(T... items) {}

    @SafeVarargs // OK: constructor
    public <T> SafeVarargsDemo(T... items) {}

    // COMPILER ERROR: Cannot apply @SafeVarargs to non-final instance method!
    // @SafeVarargs
    // public <T> void invalidMethod(T... items) {}
}

Meta-Annotations (java.lang.annotation)

Meta-annotations are annotations that apply to custom annotation declarations (@interface).

Meta-AnnotationPurposeKey Parameters / Values
@TargetRestricts which Java elements the annotation can be placed on.ElementType enum constants: TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER, TYPE_USE, MODULE, RECORD_COMPONENT.
@RetentionSpecifies how long the annotation is retained.RetentionPolicy constants: SOURCE, CLASS (default if omitted!), RUNTIME.
@DocumentedCauses the annotation to be included in Javadoc-generated API documentation.No parameters (marker annotation).
@InheritedCauses subclasses to automatically inherit the annotation when placed on a class declaration. (Applies only to class inheritance, not interfaces!).No parameters.
@RepeatableAllows the annotation to be applied multiple times to the same declaration.Takes the containing annotation class (e.g., @Repeatable(Tags.class)).

The Three RetentionPolicy Levels

  1. RetentionPolicy.SOURCE: Retained only in source code; discarded by the compiler during bytecode compilation (e.g., @Override, @SuppressWarnings).
  2. RetentionPolicy.CLASS: Recorded in .class bytecode by the compiler, but not loaded into memory by the JVM at runtime. This is the default retention policy if no @Retention meta-annotation is present!
  3. RetentionPolicy.RUNTIME: Recorded in .class bytecode and retained by the JVM at runtime, making it discoverable via reflection (Class.getAnnotation(), Method.isAnnotationPresent()).

Declaring Custom Annotations

Custom annotations are declared using the @interface keyword. Annotation elements are declared as method-like signatures without bodies.

Valid Return Types for Annotation Elements

[!WARNING] The Java language specification strictly limits what types an annotation element can return. Valid return types are:

  • Primitive types (int, double, boolean, char, byte, short, long, float)
  • java.lang.String
  • java.lang.Class (or parameterized Class<?>)
  • enum types
  • Another annotation type
  • A one-dimensional array of any of the above types (e.g., String[], int[], Target[])

FORBIDDEN TYPES: Wrapper classes (Integer, Double), multidimensional arrays (String[][]), arbitrary Objects, Collections (List<String>), or void will cause a COMPILATION ERROR.

import java.lang.annotation.*;

public enum Priority { LOW, MEDIUM, HIGH }

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ServiceEndpoint {
    // Valid element declarations with default values
    String path();
    Priority priority() default Priority.MEDIUM;
    int timeoutMs() default 5000;
    Class<?> responseType() default Object.class;
    String[] tags() default {};

    // COMPILER ERRORS if uncommented:
    // Integer maxRetries();       // Error: Wrapper types not allowed
    // List<String> permissions(); // Error: Collections not allowed
    // String[][] matrix();        // Error: 2D arrays not allowed
    // void execute();             // Error: void not allowed
}

Single-Element Shortcut (value())

If an annotation has an element named value(), callers can omit the element name when providing only that single attribute:

public @interface Cacheable {
    String value(); // Special element name
    int ttlSeconds() default 60;
}

// Shorthand usage:
@Cacheable("user_cache") // Equivalent to @Cacheable(value = "user_cache")
public class UserService {}

// When specifying multiple attributes, element names are required:
@Cacheable(value = "user_cache", ttlSeconds = 300)
public class OrderService {}

Deep Dive: Type Annotations (JSR 308)

Prior to Java 8, annotations could only be placed on declarations (classes, fields, methods, parameters). Java 8 introduced Type Annotations (JSR 308) via two specialized ElementType targets in java.lang.annotation.ElementType:

  • ElementType.TYPE_USE: Allows the annotation to be applied to any use of a type, including generic type arguments, type casts, new expressions, implements clauses, and throws clauses.
  • ElementType.TYPE_PARAMETER: Allows the annotation to be applied to generic type parameter declarations (e.g., <@Immutable T>).

Where TYPE_USE Annotations Can Appear

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.util.List;
import java.util.Map;

@Target(ElementType.TYPE_USE)
@interface NonNull {}

@Target(ElementType.TYPE_USE)
@interface ReadOnly {}

@Target(ElementType.TYPE_PARAMETER)
@interface ValidType {}

// 1. Generic class type parameter declaration
public class TypeAnnotationDemo<@ValidType T> {

    // 2. Generic type argument in field declarations
    private List<@NonNull String> names;
    private Map<@NonNull String, @ReadOnly List<@NonNull Integer>> mapping;

    // 3. Nested array dimension annotations
    // Indicates an array of NonNull Strings vs a NonNull array of Strings:
    private String @NonNull [] nonNullArray; // The array reference itself cannot be null
    private @NonNull String [] nonNullElements; // The String elements inside cannot be null

    // 4. Type cast and instanceof expressions
    public void process(Object obj) {
        String str = (@NonNull String) obj; // Type cast annotation
    }

    // 5. Method return types, parameters, and throws clauses
    public @NonNull String getRecord() throws @ReadOnly Exception {
        return "data";
    }

    // 6. Object creation with 'new'
    public void create() {
        Object obj = new @NonNull String("hello");
    }
}

Repeating Annotations (@Repeatable)

Java 8+ allows the same annotation to be repeated multiple times on the same declaration or type use if the annotation is decorated with the @Repeatable meta-annotation.

To declare a repeatable annotation, you must define two annotation interfaces:

  1. The repeatable annotation itself, meta-annotated with @Repeatable(ContainingType.class).
  2. The containing (container) annotation type, which must have a value() element returning an array of the repeatable annotation type.

[!IMPORTANT] The container annotation must have identical or broader retention and target scopes:

  • The container annotation's @Retention must be at least as long-lived as the repeatable annotation (e.g., if @Schedule is RUNTIME, @Schedules must also be RUNTIME).
  • The container annotation's @Target must include all targets defined on the repeatable annotation.
import java.lang.annotation.*;

// 1. Declare the repeatable annotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Repeatable(Schedules.class) // Points to the container annotation
public @interface Schedule {
    String dayOfWeek();
    int hour() default 0;
}

// 2. Declare the container annotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Schedules {
    Schedule[] value(); // Must return an array of Schedule!
}

// Usage: Multiple annotations on a single method
public class BatchJob {
    @Schedule(dayOfWeek = "MON", hour = 8)
    @Schedule(dayOfWeek = "FRI", hour = 17)
    public void executePayroll() {
        // Automatically wrapped in @Schedules container at compile time
    }
}
Loading diagram...
Annotation Lifecycles Across Retention Policies
Test Your Knowledge

Which of the following method declarations can legally be annotated with @SafeVarargs?

A
B
C
D
Test Your Knowledge

What is the default retention policy for a custom annotation if the @Retention meta-annotation is omitted from its declaration?

A
B
C
D
Test Your Knowledge

Which of the following element declarations inside a custom @interface definition causes a COMPILATION ERROR?

A
B
C
D
Test Your Knowledge

Consider the following annotation declaration: @Deprecated(since = "21", forRemoval = true). What does forRemoval = true communicate to the compiler and developers?

A
B
C
D