Annotations and Metadata
Read common annotations, declare a small custom annotation, and understand how metadata becomes available at runtime.
Why it matters
Start from the concept, not the syntax
Meet a concrete situation where this idea earns its place, then state the boundary it guards.
Annotations attach structured metadata to declarations. They do not execute behavior until a compiler, tool, or runtime consumer reads them. Understanding targets, retention, and inspection keeps reflection safe.
By the end of this chapter
Assuming annotations perform work by themselves leads to silent bugs where unread annotations fail to enforce expected behaviors.
- @Override compiler checks
- @interface declarations
- Retention policies
Mechanism
How Java expresses the concept
Walk the actual types, rules, and runtime behavior involved—the same behavior described in the Java Language Specification.
The mental model
Built-in annotations such as @Override, @Deprecated, and @SuppressWarnings communicate intent to the compiler or tools. @Override catches a mismatched method signature at compile time.
A custom annotation is declared with @interface. @Target limits where it may appear, while @Retention controls whether it is kept only in source, in class files, or at runtime.
Annotation metadata is separate from the annotated object. Adding an annotation does not validate input, create an object, or change control flow by itself.
Use annotations for stable metadata and keep business rules in ordinary methods that can be read and tested directly.
Concept map
Inspect the building blocks as a whole
Use the map as a structural reference; it shows the pieces and their relationships, not an execution sequence.
Core Java
Annotations and metadata
Annotations attach structured metadata to declarations. They do not execute behavior until a compiler, tool, or runtime consumer reads them.
- 01@Override and compiler checks
- 02@interface declarations
- 03@Target locations
- 04@Retention policies
- 05Runtime metadata
Implementation
Encode the smallest runnable example
Write and run the code, then observe the behavior you just predicted with a real Java program.
Runnable snippet path
Predict, run, and change each example
Protect an override
The compiler catches a mismatched method name or parameter list.
interface Notification {
String message();
}
final class EmailNotification implements Notification {
@Override
public String message() { return "email"; }
}Remember
Keep @Override on every intended override.
Change one input, predict the result, then compile or test it before continuing.
Declare runtime metadata
RUNTIME retention makes metadata discoverable through reflection.
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Audited {}
class AccountService {
@Audited void closeAccount() {}
}Remember
An annotation has no effect until a consumer gives it meaning.
Change one input, predict the result, then compile or test it before continuing.
Watch out
Break it on purpose, then repair the right layer
Predict the symptom before running the failure case, and close with the completion checklist.
Deliberate failure
Break
Place a custom annotation on a method without a runtime consumer or retention policy
Observe
The annotation has no effect and reflection fails to find it at runtime
Repair
Set @Retention(RetentionPolicy.RUNTIME) when reading through reflection and remember annotations need active consumers
Why it matters
Set @Retention(RetentionPolicy.RUNTIME) when reading through reflection and remember annotations need active consumers
Completion checklist
- Use @Override to verify supertype signatures
- Declare custom annotations with appropriate @Target
- Select the correct @Retention lifecycle
Go deeper
Official references
The primary sources behind this chapter. Every claim above stays consistent with them.