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.
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: AStringspecifying the version in which the element was deprecated (e.g.,since = "21").forRemoval: Abooleanindicating whether the element is scheduled for permanent deletion in a future release (default isfalse).
@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]
@SafeVarargscan ONLY be applied to methods that cannot be overridden:
staticmethodsfinalinstance methodsprivateinstance methods (allowed since Java 9)- Constructors
Applying
@SafeVarargsto apublic,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-Annotation | Purpose | Key Parameters / Values |
|---|---|---|
@Target | Restricts 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. |
@Retention | Specifies how long the annotation is retained. | RetentionPolicy constants: SOURCE, CLASS (default if omitted!), RUNTIME. |
@Documented | Causes the annotation to be included in Javadoc-generated API documentation. | No parameters (marker annotation). |
@Inherited | Causes subclasses to automatically inherit the annotation when placed on a class declaration. (Applies only to class inheritance, not interfaces!). | No parameters. |
@Repeatable | Allows 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
RetentionPolicy.SOURCE: Retained only in source code; discarded by the compiler during bytecode compilation (e.g.,@Override,@SuppressWarnings).RetentionPolicy.CLASS: Recorded in.classbytecode by the compiler, but not loaded into memory by the JVM at runtime. This is the default retention policy if no@Retentionmeta-annotation is present!RetentionPolicy.RUNTIME: Recorded in.classbytecode 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.Stringjava.lang.Class(or parameterizedClass<?>)enumtypes- 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>), orvoidwill 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,newexpressions,implementsclauses, andthrowsclauses.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:
- The repeatable annotation itself, meta-annotated with
@Repeatable(ContainingType.class). - 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
@Retentionmust be at least as long-lived as the repeatable annotation (e.g., if@ScheduleisRUNTIME,@Schedulesmust also beRUNTIME).- The container annotation's
@Targetmust 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
}
}
Which of the following method declarations can legally be annotated with @SafeVarargs?
What is the default retention policy for a custom annotation if the @Retention meta-annotation is omitted from its declaration?
Which of the following element declarations inside a custom @interface definition causes a COMPILATION ERROR?
Consider the following annotation declaration: @Deprecated(since = "21", forRemoval = true). What does forRemoval = true communicate to the compiler and developers?