skip to content

Meta-Annotations & Retention

@Retention decides whether an annotation survives to runtime, @Target restricts where it can be applied, and @Inherited and @Repeatable cover the rest. The retention policy is the one interviewers ask about, because SOURCE and CLASS retention are invisible to reflection.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

5

What are the three @Retention policies in Java (SOURCE, CLASS, RUNTIME) and how does each affect when an annotation is available?

level: middleimportance: must knowfreq 70%

answer

  1. SOURCE -> CLASS -> RUNTIME = compiler-only / bytecode / reflection-visible
  2. CLASS is the silent default
  3. Frameworks need RUNTIME
  4. Reflection sees only RUNTIME
  5. @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 lines
java
import 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

for a junior

Knows the three names and that RUNTIME is the one frameworks read with reflection.

for a middle

Can explain each policy across the source->bytecode->runtime stages and that CLASS is the default.

for a senior

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).

for a principal

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).

context

open as a page

What is @Documented, and how do you read annotation values at runtime via reflection?

level: juniorimportance: should knowfreq 38%

basics

~20 s

@Documented makes an annotation show up in the generated Javadoc of whatever it is applied to. To read an annotation's values while the program runs, you use reflection (getAnnotation) and call the annotation's element methods — but only if it is kept at RUNTIME.

open as a page

What does @Target do, and what happens if a custom annotation has no @Target?

level: middleimportance: should knowfreq 55%

basics

~10 s

@Target restricts which program elements an annotation can be placed on, like methods or fields, using ElementType values. With no @Target, the annotation can be used almost everywhere.

open as a page

What does @Inherited do, and what are its precise limitations?

level: seniorimportance: should knowfreq 45%

basics

~10 s

@Inherited makes a class-level annotation be seen on subclasses too, when you ask via reflection. It only works for annotations on classes — not on methods, fields, or interfaces.

open as a page

How does @Repeatable work, and what is the role of the container annotation?

level: seniorimportance: should knowfreq 40%

basics

~10 s

@Repeatable (Java 8) lets you put the same annotation on one element multiple times. You also declare a 'container' annotation that holds an array of them; the compiler bundles the repeats into that container.

open as a page