Annotations

1. What Is an Annotation?

Common uses:

Compiler → detect errors / suppress warnings
Tools    → generate code or files
Runtime  → inspect metadata

2. Basic Annotation Syntax

An annotation starts with @:

@Override
void print() {
}

Annotations can also have elements:

@Author(name = "John", date = "2026")
class User {
}

If there is only one element named value, its name can be omitted:

@SuppressWarnings("unchecked")

Instead of:

@SuppressWarnings(value = "unchecked")

If an annotation has no elements, parentheses are unnecessary:

@Override

3. Where Can Annotations Be Used?

Annotations can be applied to many Java elements:

Classes
Methods
Fields
Constructors
Parameters
Local variables
Packages
Modules
Type parameters
Record components
Types

Example:

@Deprecated
class OldClass {
}
@Deprecated
void oldMethod() {
}

Since Java 8, annotations can also be applied directly to types:

@NonNull String name;
List<@NonNull String> names;

This is called a type annotation.


4. Creating Your Own Annotation

You can define a custom annotation using @interface:

public @interface Author {
    String name();
    String date();
}

Then use it:

@Author(
    name = "John Doe",
    date = "2026"
)
class User {
}

Annotation elements can have default values:

public @interface Author {
    String name();
    String date();
    int version() default 1;
}

Now version is optional:

@Author(
    name = "John",
    date = "2026"
)
class User {
}

5. Common Built-in Annotations

@Override

Tells the compiler that a method is intended to override a superclass method.

@Override
public void display() {
}

If the method doesn't actually override anything, the compiler reports an error.


@Deprecated

Marks something as old or discouraged from being used.

@Deprecated
void oldMethod() {
}

Using it can produce a compiler warning.

Since Java 9, it can also indicate planned removal:

@Deprecated(forRemoval = true)
void oldMethod() {
}

@SuppressWarnings

Tells the compiler to suppress specific warnings.

@SuppressWarnings("unchecked")
void process() {
}

Multiple warnings:

@SuppressWarnings({"unchecked", "deprecation"})
void process() {
}

@FunctionalInterface

Indicates that an interface is intended to have exactly one abstract method.

@FunctionalInterface
interface Calculator {
    int calculate(int a, int b);
}

It helps the compiler detect accidental changes that would make the interface non-functional.


@SafeVarargs

Used with certain generic varargs methods to indicate that the method performs no unsafe operations on its varargs parameter.

@SafeVarargs
static <T> void print(T... values) {
    for (T value : values) {
        System.out.println(value);
    }
}

6. Meta-Annotations

Meta-annotations are annotations used to describe how another annotation behaves.

@Retention

Specifies how long an annotation should be retained.

SOURCE  → available only in source code
CLASS   → stored in .class files
RUNTIME → available at runtime through reflection

Example:

@Retention(RetentionPolicy.RUNTIME)
@interface Author {
    String name();
}

@Target

Specifies where an annotation can be used.

@Target(ElementType.METHOD)
@interface Important {
}

Now:

@Important
void doSomething() {
}

is valid, while using @Important on an unsupported element is not.

Common targets include:

TYPE
METHOD
FIELD
PARAMETER
CONSTRUCTOR
LOCAL_VARIABLE
TYPE_PARAMETER
TYPE_USE

@Documented

Specifies that an annotation should appear in generated Javadoc.

@Documented
@interface Author {
    String name();
}

@Inherited

Allows a class-level annotation to be inherited by subclasses.

@Inherited
@interface Important {
}

Then:

@Important
class Parent {
}

class Child extends Parent {
}

Child can inherit the annotation from Parent.


@Repeatable

Allows the same annotation to be used multiple times on the same declaration.

@Schedule(day = "Monday")
@Schedule(day = "Friday")
void cleanup() {
}

This is useful when one annotation needs to hold multiple configurations.


7. Type Annotations

Java 8 introduced the ability to place annotations wherever a type is used.

For example:

@NonNull String name;

Inside generics:

List<@NonNull String> names;

On a cast:

String value = (@NonNull String) obj;

These annotations can be used by external tools such as type-checking frameworks to provide additional compile-time checks.

For example:

@NonNull String name;

can communicate:

"name should never be null"

The annotation itself doesn't automatically enforce this unless a tool/framework processes it.


8. Repeating Annotations

Java allows an annotation to be applied multiple times when it is declared with @Repeatable.

Example:

@Schedule(day = "Monday")
@Schedule(day = "Friday")
void cleanup() {
}

A repeatable annotation normally has a container annotation behind it.

Conceptually:

@Schedule
@Schedule
   ↓
container annotation
   ↓
stores multiple Schedule annotations

This allows APIs to represent multiple pieces of the same kind of metadata cleanly.


9. Annotations vs. Comments

A comment:

// This method calculates the total

is mainly for humans.

An annotation:

@Override

has a defined meaning that Java tools/compiler/runtime can process.

So:

Comment
→ information for humans

Annotation
→ structured metadata for tools/compiler/runtime

Quick Revision

Feature Purpose Example
Annotation Add metadata @Override
Custom annotation Define your own metadata @interface Author
@Override Verify method overriding @Override
@Deprecated Mark old API @Deprecated
@SuppressWarnings Suppress compiler warnings @SuppressWarnings("unchecked")
@FunctionalInterface Enforce functional interface @FunctionalInterface
@SafeVarargs Mark safe generic varargs @SafeVarargs
@Retention Control annotation lifetime RUNTIME
@Target Restrict where annotation can be used METHOD
@Documented Include in Javadoc @Documented
@Inherited Inherit class annotation @Inherited
@Repeatable Allow repeated annotations @Schedule
Type annotation Annotate a type usage List<@NonNull String>

In short

Annotation
    ↓
Metadata about your code
    ↓
Compiler / Tools / Runtime
    ↓
Can check, process, generate, or interpret information

The most important distinction to remember is:

@Override tells the compiler something about your code, while custom annotations allow you to define your own metadata that tools or your program can process.