What are the three @Retention policies in Java (SOURCE, CLASS, RUNTIME) and how does each affect when an annotation is available?
answer
- SOURCE -> CLASS -> RUNTIME = compiler-only / bytecode / reflection-visible
- CLASS is the silent default
- Frameworks need RUNTIME
- Reflection sees only RUNTIME
- @Override is SOURCE; @Audited for Spring is RUNTIME
basics
~20 s@Retention says how long an annotation is kept. SOURCE: discarded after compiling. CLASS (the default): stored in the .class file but not loaded at runtime. RUNTIME: kept and readable via reflection while the program runs.
solid answer
~40 s@Retention is a meta-annotation (an annotation placed on another annotation) that controls the lifetime of the annotated annotation, via a RetentionPolicy enum value. SOURCE means it exists only in source and is dropped by the compiler, used by compile-time tools like @Override or Lombok. CLASS, the default when you omit @Retention, keeps it in the .class bytecode but the JVM does not load it, so reflection cannot see it; only bytecode tools can. RUNTIME keeps it and the JVM loads it, so you can read it with reflection at runtime via getAnnotation. If you want a framework like Spring or JUnit to discover annotations by reflection, you must use RUNTIME. Too broad a policy keeps useless metadata around; too narrow makes reflection silently return nothing.
code
java · 9 linesimport java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@interface Audited {}
class Service { @Audited void pay() {} }
// boolean seen = Service.class.getDeclaredMethod("pay")
// .isAnnotationPresent(Audited.class); // true (RUNTIME)go deeper
Knows the three names and that RUNTIME is the one frameworks read with reflection.
Can explain each policy across the source->bytecode->runtime stages and that CLASS is the default.
Reasons about why a custom annotation is silently invisible to reflection and picks the right policy for a given consumer (compiler vs bytecode tool vs framework).
Weighs retention trade-offs at API-design scale — metadata footprint, tooling ecosystems (annotation processors vs bytecode weaving vs reflective scanning), and sets conventions for a codebase's custom annotations.
## What is an annotation? An **annotation** is a marker attached to code (a class, method, field, parameter, etc.), written with an `@` sign, e.g. `@Override`. It is *metadata*: information about the code that does nothing by itself. Some other party (the compiler, a framework, a tool) reads it and acts on it. ## What is a meta-annotation? A **meta-annotation** is an annotation placed on the *declaration of another annotation*. The standard ones live in `java.lang.annotation`: `@Retention`, `@Target`, `@Documented`, `@Inherited`, `@Repeatable`. They configure how the annotation you are defining behaves. `@Retention` is the most important one. ## What @Retention controls `@Retention` decides **how long the annotation survives** as your code moves through three stages: source code -> compiled `.class` bytecode -> running JVM. Its value is one of the `RetentionPolicy` enum constants. - **SOURCE**: The annotation exists only in the `.java` source. The compiler reads it (if it wants) and then *throws it away* — it never appears in the `.class` file. Use this for things only the compiler or source tools need: `@Override`, `@SuppressWarnings`, and Lombok annotations like `@Getter` (Lombok runs during compilation, generates code, then the annotation is gone). - **CLASS**: The annotation is written into the `.class` bytecode file, but when the JVM loads the class it *does not* keep the annotation in memory, so **reflection cannot see it**. This is the **default** — if you write a custom annotation and omit `@Retention`, you get CLASS. Tools that read bytecode directly (bytecode weavers, some analyzers) can still see it. - **RUNTIME**: The annotation is in the bytecode *and* the JVM loads it into the runtime class metadata, so it is **visible to reflection** while the program runs. This is what frameworks need: Spring, JUnit, Jackson, JPA all scan classes with reflection (`clazz.getAnnotation(...)`, `isAnnotationPresent(...)`), so their annotations are RUNTIME. ## How retention is checked At runtime you read annotations through the **Reflection API**: methods like `Class.getAnnotation(MyAnno.class)`, `Method.getAnnotations()`, `isAnnotationPresent(...)`. These return data **only for RUNTIME-retained** annotations. A SOURCE or CLASS annotation will make these methods return `null`/empty with no error — a classic silent bug. ## Choosing a policy Rule of thumb: if anything reads the annotation via reflection at runtime, use RUNTIME. If only the compiler/source tooling needs it, use SOURCE. CLASS is rarely chosen on purpose; you mostly get it by accident as the default. Picking too broad wastes a little space and keeps metadata you do not need; picking too narrow is worse because reflection quietly returns nothing. ## Worked example ```java import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @interface Audited {} class Service { @Audited void pay() {} } // elsewhere: Method m = Service.class.getDeclaredMethod("pay"); boolean isAudited = m.isAnnotationPresent(Audited.class); // true ONLY because RUNTIME ``` If `@Audited` had no `@Retention` (defaulting to CLASS) or SOURCE, `isAnnotationPresent` would return `false`.
- What is the default RetentionPolicy if you omit @Retention on a custom annotation?CLASS. The annotation is written to the .class file but not loaded by the JVM, so reflection cannot see it — a common cause of 'my framework ignores my annotation' bugs.
- Why is @Override declared with SOURCE retention?Its only job is a compile-time check that a method actually overrides a supertype method. Nothing needs it after compilation, so keeping it in bytecode or at runtime would be wasted metadata.
Think of a sticky note on a document. SOURCE = note read during drafting then tossed. CLASS = note photocopied into the archived file but never pulled out again. RUNTIME = note kept on the live document so anyone working with it can read it anytime.
saying these in an interview costs you the question
- Saying the default retention is RUNTIME (it is CLASS).
- Claiming reflection can read CLASS- or SOURCE-retained annotations.
- Thinking SOURCE annotations end up in the .class file.
- Assuming retention affects what the annotation can be applied to (that is @Target).