For codegeneration tools, like Lombok
🌐 What is RetentionPolicy.CLASS
?
Definition
@Retention(RetentionPolicy.CLASS)
means the annotation is: ✅ Present in the compiled .class
file
❌ Not available at runtime (no reflection access)
🔎 When is this Useful?
This is ideal when:
- You want an annotation to exist in the
.class
file for tools like bytecode analyzers (e.g., frameworks, tools that enhance or modify bytecode — like Hibernate or AspectJ). - But you don’t need it at runtime (your actual running program will never read this annotation).
📚 Example — Tracking Generated Code (Compile-Time Annotation)
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
@Retention(RetentionPolicy.CLASS) // Exists in .class file but not at runtime
@Target(ElementType.TYPE)
public @interface Generated {
String value();
}
Example Usage
@Generated("MyCodeGenerator")
public class AutoGeneratedClass {
// class body
}
🔬 What happens to this annotation?
Stage | Is Annotation Available? |
---|---|
Source code | ✅ Yes |
Bytecode (.class file) | ✅ Yes |
At runtime (via reflection) | ❌ No — reflection cannot see it |
🛠️ Why is this Useful?
This can be useful for: ✅ Code generation tools (annotation processors).
✅ Static analysis tools that scan compiled .class
files.
✅ Enhancers like Hibernate’s proxy generation or AspectJ’s weaving tools.
Example: A bytecode enhancer can detect this annotation in the .class
file and add extra logging or tracking code.
❌ Why Not Use RUNTIME
?
- If your app does not need to read the annotation at runtime, using
RUNTIME
is overkill. CLASS
annotations are lighter — they don’t occupy JVM metadata space at runtime.- It’s a performance/memory optimization in some cases.
📊 Comparison with Other Retentions
Retention | Exists in Source? | Exists in Class File? | Available via Reflection? |
---|---|---|---|
SOURCE | ✅ Yes | ❌ No | ❌ No |
CLASS | ✅ Yes | ✅ Yes | ❌ No |
RUNTIME | ✅ Yes | ✅ Yes | ✅ Yes |
🔥 Real-World Examples
Annotation | Retention | Why? |
---|---|---|
@Override | SOURCE | Needed only by the compiler |
@Entity (JPA) | RUNTIME | Needed at runtime for ORM mapping |
@Generated | CLASS | Useful for tracking code generation, but not needed at runtime |
⚠️ Important Notes
RetentionPolicy.CLASS
is the default retention if you don’t explicitly specify@Retention
.- Many tools that generate proxies, instrumentation, or bytecode enhancements rely on annotations with
CLASS
retention.
📣 Summary Rule for Interviews
✅ RetentionPolicy.CLASS
is ideal when:
- Annotations are needed for compilation or bytecode processing.
- But not for runtime reflection.
- It’s a balance between preserving metadata for tools and keeping runtime memory clean.
🎯 Final Pro Tip
If asked in interviews:
“When would you use
RetentionPolicy.CLASS
?”
✅ Example Answer:
“For annotations used by bytecode manipulation tools, like Hibernate’s enhancer or tools that track generated code. The annotations don’t need to be readable at runtime, so keeping them only in the class file saves memory.”
Let’s build a complete example where we:
✅ Define a custom annotation with @Retention(RetentionPolicy.CLASS)
✅ Use that annotation in a real class
✅ Write a simple annotation processor to detect this annotation at compile time and generate some output (like a report or log)
🔨 Step 1 — Create the Annotation
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
@Retention(RetentionPolicy.CLASS) // Exists in .class file, but not at runtime
@Target(ElementType.TYPE) // Can only be applied to classes
public @interface Generated {
String value();
}
🏗️ Step 2 — Use the Annotation
@Generated("MyCodeGenerator")
public class MyGeneratedClass {
public void process() {
System.out.println("Processing in generated class...");
}
}
⚙️ Step 3 — Write an Annotation Processor
This is a compile-time processor that will inspect all classes with @Generated
and print their names. Since the retention is CLASS
, this happens at compile time — not runtime.
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;
import java.util.Set;
@SupportedAnnotationTypes("Generated")
@SupportedSourceVersion(SourceVersion.RELEASE_17)
public class GeneratedAnnotationProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (Element element : roundEnv.getElementsAnnotatedWith(Generated.class)) {
System.out.println("Found @Generated on: " + element.getSimpleName());
}
return true; // Claim the annotation so no other processor will handle it
}
}
📜 Explanation
AbstractProcessor
runs at compile time.- It looks for all classes annotated with
@Generated
. - Since
@Generated
isRetentionPolicy.CLASS
, the annotation:- Exists in source during compilation.
- Gets compiled into the .class file.
- Cannot be accessed via reflection after the program runs.
📦 Directory Structure (for clarity)
src/
├── Generated.java // The annotation
├── MyGeneratedClass.java // A class that uses it
└── GeneratedAnnotationProcessor.java // The processor
🚀 Step 4 — Build and Compile with Annotation Processing
In real projects, this is wired via javac options or a build tool like Maven/Gradle. Example for direct javac
command:
javac -processorpath . -processor GeneratedAnnotationProcessor *.java
🧰 Example Output (during compilation)
Found @Generated on: MyGeneratedClass
❌ What Happens at Runtime?
At runtime, if you try this:
Class<?> clazz = MyGeneratedClass.class;
Annotation[] annotations = clazz.getAnnotations();
System.out.println("Annotations found: " + Arrays.toString(annotations));
✅ Output:
Annotations found: []
🔥 Summary
Lifecycle Stage | What Happens? |
---|---|
Compile Time | Annotation processor detects @Generated |
Class File | Annotation is present in bytecode |
Runtime | Annotation is not accessible via reflection |
💡 Real-World Case
This is exactly the kind of thing tools like:
- Lombok
- MapStruct
- Dagger
… do internally — they generate code and tag it with annotations like @Generated
, which gets recorded in the class file for tracking but disappears at runtime.
🎯 Final Pro Tip for Interviews
✅ When asked why RetentionPolicy.CLASS
exists, you can say:
“It’s for annotations needed by the compiler or build tools, but that do not need to waste memory at runtime. Great for code generation, instrumentation, and compiler plugins.”