Types of Annotations
About
Annotations in Java are not just syntactic sugar but they are a powerful mechanism to add metadata that the compiler, tools, or frameworks can interpret. Understanding their types helps us write more expressive, readable, and maintainable code.
We can classify Java annotations broadly into several types based on purpose, structure, and usage scope.
1. Standard Annotations (Built-in Language Annotations)
These are provided by Java itself and typically influence compiler behavior.
Examples
@Override
Ensures a method is overriding a superclass method.
@Deprecated
Indicates that a method or class should no longer be used.
@SuppressWarnings
Tells the compiler to ignore specific warnings (like unchecked casts).
@FunctionalInterface
Marks an interface as having a single abstract method.
@SafeVarargs
Suppresses heap pollution warnings for varargs with generics.
These annotations improve readability, compile-time safety, and documentation clarity.
2. Meta-Annotations
Meta-annotations are annotations on other annotations. They define the behavior, scope, and life cycle of custom annotations.
Key Meta-Annotations
@Target
Specifies where the annotation can be applied (method, field, class, etc.).
@Retention
Defines how long the annotation should be retained (SOURCE, CLASS, RUNTIME).
@Documented
Indicates that the annotation should appear in Javadocs.
@Inherited
Allows annotations to be inherited by subclasses.
@Repeatable
Allows an annotation to be applied multiple times to the same element.
These are crucial when creating our own annotations.
3. Custom Annotations
These are annotations defined by the developer for specific use cases, often processed using reflection or annotation processors.
Example
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {
}
We can then process @LogExecutionTime
in our application logic (e.g., with Spring AOP or a custom processor) to add behavior like measuring method execution time.
Custom annotations are often used in frameworks, validations, event handling, or configuration.
4. Framework-Specific Annotations
Modern Java frameworks (like Spring, JPA, Jackson) heavily rely on annotations to provide declarative programming.
Examples by Framework
Spring:
@Component
,@Autowired
,@RequestMapping
,@Transactional
JPA (Hibernate):
@Entity
,@Table
,@Id
,@Column
Jackson (for JSON mapping):
@JsonProperty
,@JsonIgnore
,@JsonInclude
JUnit:
@Test
,@BeforeEach
,@AfterAll
These annotations configure application behavior, manage dependencies, or handle persistence—all without writing boilerplate code.
5. Marker Annotations
Marker annotations are annotations without any methods. Their presence alone conveys meaning.
Examples:
@Deprecated
@Override
Custom:
@Secure
,@Trackable
, etc.
They act like boolean flags: the mere presence of the annotation is enough to trigger logic.
6. Single-Value Annotations
These annotations have only one method, usually named value()
, allowing the user to omit the key when assigning a value.
public @interface Role {
String value();
}
@Role("ADMIN") // no need to write value = "ADMIN"
They provide a concise syntax while still carrying metadata.
7. Multi-Value Annotations
Multi-value annotations allow for multiple named attributes, making them versatile for complex metadata.
public @interface Config {
String name();
int version();
}
@Config(name = "AppX", version = 2)
These are ideal for declarative configuration, such as defining API routes, validation rules, or policies.
8. Repeatable Annotations
Prior to Java 8, the same annotation couldn’t be applied more than once to the same element. With @Repeatable
, this is now possible.
@Schedule(day = "Monday")
@Schedule(day = "Friday")
public void generateReport() {}
We must mark the annotation with @Repeatable(SomeContainer.class)
to enable this.
Last updated