Annotations: Metadata on Your Code

Introduction

Annotations attach metadata to classes, methods, fields, and parameters. Tools (compiler, IDE, frameworks) read them for validation, documentation, or runtime behavior. You already used @Override in Inheritance; this chapter surveys built-in annotations, simple custom ones, and @FunctionalInterface before Functional Programming.

Prerequisites

What Is an Annotation

Syntax: @AnnotationName or @AnnotationName(value)

java
@Override
public String toString() {
    return "Demo";
}

Annotations do not change logic by themselves unless a processor or framework acts on them.

1) Built-in Annotations

AnnotationTarget (typical)Purpose
@Overridemethodcompiler checks override
@Deprecatedclass, method, fieldmarks obsolete API
@SuppressWarningsvarioussilence compiler warnings (use sparingly)
@FunctionalInterfaceinterfaceone abstract method contract
@SafeVarargsmethod/constructorvarargs generics warning

@Override

java
@Override
public boolean equals(Object obj) {
    // ...
}

If parent signature changes, compiler error instead of silent bug.

@Deprecated

java
@Deprecated
public void oldApi() { }
 
// preferred replacement documented in Javadoc

@SuppressWarnings

java
@SuppressWarnings("unchecked")
List raw = new ArrayList();

Avoid blanket suppression—fix root cause when possible.

@FunctionalInterface

java
@FunctionalInterface
public interface Operation {
    int apply(int a, int b);
}

Compiler errors if a second abstract method appears.

2) Where Annotations Apply

Element types (meta-annotations on custom annotations):

  • TYPE — class, interface, enum
  • METHOD, FIELD, PARAMETER, CONSTRUCTOR
  • LOCAL_VARIABLE, etc.

3) Simple Custom Annotation

java
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
 
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Timed {
}
java
public class Service {
    @Timed
    public void runTask() {
        System.out.println("Running...");
    }
}

Retention policies:

  • SOURCE — discarded by compiler (e.g. @Override)
  • CLASS — in bytecode, not runtime reflection by default
  • RUNTIME — visible via reflection (frameworks)

Reading annotations at runtime uses reflection—advanced; frameworks like Spring/JUnit do this for you.

4) Annotation Parameters

java
public @interface Author {
    String name();
    String date();
}
 
@Author(name = "Team", date = "2026-05-17")
public class DocSample { }

Default values possible in annotation declaration.

@FunctionalInterface documents Lambda targets:

java
@FunctionalInterface
interface Printer {
    void print(String msg);
}
 
Printer p = msg -> System.out.println(msg);

Common Beginner Mistakes

Missing @Override When Overriding

Risk of accidental overload instead of override.

Custom Annotation Without Retention

Reflection at runtime needs RUNTIME.

Overusing @SuppressWarnings

Hides real problems—narrow scope and document why.

Mini Practice

Create @Tag(String value) on methods; print tags in a demo runner via reflection (optional stretch) or just practice syntax.

What’s Next

Lambdas and Streams.

FAQ

Do annotations slow programs?

Negligible. Runtime frameworks scan once at startup for many apps.

Are annotations only for frameworks?

No. They help documentation, static analysis, and team conventions too.

Difference between annotation and comment?

Comments are ignored by compiler. Annotations are structured metadata processors can read.

What reads @Test in JUnit?

JUnit runtime scans classes and runs annotated methods—example of framework processing.

Can I create annotations without reflection?

Yes—for SOURCE/CLASS retention used by compile-time tools only.