Java.Core.Provide example where we need class retention

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?

StageIs 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

RetentionExists in Source?Exists in Class File?Available via Reflection?
SOURCE✅ Yes❌ No❌ No
CLASS✅ Yes✅ Yes❌ No
RUNTIME✅ Yes✅ Yes✅ Yes

🔥 Real-World Examples

AnnotationRetentionWhy?
@OverrideSOURCENeeded only by the compiler
@Entity (JPA)RUNTIMENeeded at runtime for ORM mapping
@GeneratedCLASSUseful 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 is RetentionPolicy.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 StageWhat Happens?
Compile TimeAnnotation processor detects @Generated
Class FileAnnotation is present in bytecode
RuntimeAnnotation 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.”

This entry was posted in Без рубрики. Bookmark the permalink.