Java Annotations and Reflection
Annotations attach metadata to program elements. Reflection lets a running Java program inspect classes, methods, fields, constructors, and annotations. Together they form the basis of many frameworks, test tools, dependency-injection systems, and serialization libraries.
What is an Annotation?
An annotation is metadata attached to a program element such as a class, method, field, parameter, or constructor.
Annotations usually do not change program behavior by themselves. Instead, compilers, tools, frameworks, or reflection-based code interpret them.
Common Built-in Annotations
@Override— verifies that a method overrides or implements an inherited method.@Deprecated— marks an API that should generally no longer be used.@SuppressWarnings— asks the compiler to suppress selected warnings.@FunctionalInterface— verifies that an interface has exactly one abstract method.
class Animal {
void makeSound() {
System.out.println("Sound");
}
}
class Dog extends Animal {
@Override
void makeSound() {
System.out.println("Bark");
}
}@Override matters: if the method signature is wrong, the compiler reports an error instead of silently creating a different method.Creating a Custom Annotation
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RunThis {
}This annotation may be placed on methods and is retained at runtime, which allows reflection to discover it.
Meta-Annotations
| Meta-annotation | Purpose |
|---|---|
@Retention | Controls how long annotation metadata is kept. |
@Target | Controls which program elements may receive the annotation. |
@Documented | Allows the annotation to appear in generated Javadoc. |
@Inherited | Allows certain class-level annotations to be inherited by subclasses. |
@Repeatable | Allows the same annotation type to appear more than once on an element. |
Retention Policies
SOURCE— discarded by the compiler after compilation.CLASS— stored in the class file but not necessarily available through runtime reflection.RUNTIME— stored in the class file and available through reflection at runtime.
Annotation Elements
Annotations can contain named elements with optional default values.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TaskInfo {
String author();
int priority() default 1;
}@TaskInfo(author = "Alice", priority = 3)
public void process() {
}What is Reflection?
Reflection is the ability to inspect and interact with program structure while the application is running.
The central entry point is usually a Class<?> object.
Class<?> clazz = String.class;
System.out.println(clazz.getName());
System.out.println(clazz.getSimpleName());Inspecting and Invoking a Method
import java.lang.reflect.Method;
Class<?> clazz = Hello.class;
Object obj = clazz.getDeclaredConstructor().newInstance();
Method method = clazz.getMethod("sayHi");
method.invoke(obj);Reflection can inspect constructors, methods, fields, modifiers, parameters, generic types, and annotations.
Using Reflection with Annotations
public class Task {
@RunThis
public void doWork() {
System.out.println("Work started...");
}
public void ignoreThis() {
System.out.println("Ignore this.");
}
}Class<?> clazz = Task.class;
Object obj = clazz.getDeclaredConstructor().newInstance();
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(RunThis.class)) {
method.invoke(obj);
}
}Only methods carrying @RunThis are selected and invoked.
Reading Annotation Values
TaskInfo info =
method.getAnnotation(TaskInfo.class);
System.out.println(info.author());
System.out.println(info.priority());Why Frameworks Use This
- JUnit discovers test methods through annotations.
- Dependency-injection frameworks use metadata to identify components and dependencies.
- Web frameworks map routes and controllers through annotations.
- Serialization libraries inspect fields and configuration metadata.
Important Cautions
- Reflection is less type-safe than normal compile-time method calls.
- Reflective code is usually harder to read and debug.
- Access checks and module boundaries may restrict reflective access.
- Reflection can add runtime overhead, especially when used repeatedly without caching.
Conclusion
Annotations describe program elements with metadata, while reflection lets running code inspect that metadata and act on it. Understanding retention, targets, custom annotation elements, the Class API, and reflective method access explains how many major Java libraries and frameworks work internally.