Java Annotations


In Java, annotations are special markers that provide metadata to the compiler or runtime environment. They don’t directly affect how a program runs, but they give additional information about the code, such as instructions to the compiler or frameworks. You’ve already seen annotations like @Override, which tells Java that a method overrides a method from its superclass.

Annotations are widely used in frameworks like Spring, Hibernate, and JUnit. They help developers reduce boilerplate code and improve readability.

What Are Java Annotations?

An annotation starts with the @ symbol followed by its name. You can apply it to classes, methods, fields, parameters, or variables. For example:

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

Here, @Override tells the compiler that this method is overriding a method from its superclass. If it doesn’t, the compiler throws an error.

Annotations can serve different purposes:

  • Give instructions to the compiler (e.g., @Deprecated, @SuppressWarnings).

  • Provide metadata to runtime frameworks (e.g., @Entity, @Controller in Spring).

  • Generate code or XML configurations automatically (e.g., Lombok annotations).

Built-in Java Annotations

Java comes with several built-in annotations. Let’s look at the most common ones.

1. @Override

Used when a method overrides a method from its superclass.

class Animal {
    void sound() {
        System.out.println("Animal sound");
    }
}

class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("Dog barks");
    }
}

If the method signature in Dog didn’t match, the compiler would show an error because of @Override.

2. @Deprecated

Marks a method, class, or field as outdated. It tells the developer that there’s a better alternative available.

class Test {
    @Deprecated
    void oldMethod() {
        System.out.println("This method is deprecated");
    }

    void newMethod() {
        System.out.println("Use this instead");
    }
}

If you use oldMethod(), the compiler shows a warning.

3. @SuppressWarnings

Used to suppress compiler warnings that you know can be safely ignored.

@SuppressWarnings("unchecked")
void example() {
    List list = new ArrayList();
    list.add("Hello");
    System.out.println(list.get(0));
}

This tells the compiler not to warn about the use of raw types.

4. @SafeVarargs

This annotation is used to suppress warnings about potentially unsafe operations when using varargs with generics.

@SafeVarargs
private final <T> void printAll(T... elements) {
    for (T e : elements) {
        System.out.println(e);
    }
}

It ensures that your method doesn’t perform unsafe operations on its varargs parameters.

5. @FunctionalInterface

This annotation ensures that an interface has exactly one abstract method. It’s commonly used with lambda expressions.

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

If you add another abstract method, the compiler will throw an error.

Meta-Annotations

Meta-annotations are annotations that are applied to other annotations. They define how your annotation behaves.

1. @Target

Defines where the annotation can be applied (class, method, field, etc.).

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

2. @Retention

Defines how long annotations are retained — in source code, compiled class file, or at runtime.

@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {}

There are three retention policies:

  • SOURCE: discarded after compilation.

  • CLASS: stored in the .class file but not available at runtime.

  • RUNTIME: available to JVM at runtime (used by reflection).

3. @Inherited

Allows an annotation to be inherited by subclasses.

@Inherited
@interface MyAnnotation {}

@MyAnnotation
class Parent {}

class Child extends Parent {}  // Child inherits @MyAnnotation

4. @Documented

Marks that the annotation should appear in Javadoc.

Creating Custom Annotations

You can create your own annotations in Java. Here’s an example:

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Info {
    String author();
    String date();
    int revision() default 1;
}

Then, you can apply this annotation like this:

class Demo {
    @Info(author = "Priya", date = "2025-10-31", revision = 2)
    public void display() {
        System.out.println("Custom annotation example");
    }
}

Accessing Custom Annotations Using Reflection

import java.lang.reflect.Method;

public class Main {
    public static void main(String[] args) throws Exception {
        Method method = Demo.class.getMethod("display");
        Info info = method.getAnnotation(Info.class);
        System.out.println("Author: " + info.author());
        System.out.println("Date: " + info.date());
        System.out.println("Revision: " + info.revision());
    }
}

This prints the metadata stored in the annotation at runtime.

Why Use Annotations?

  • Cleaner code: Reduce repetitive XML or configuration files.

  • Improved readability: Metadata appears close to the code it affects.

  • Framework integration: Many modern Java frameworks rely on annotations.

  • Compile-time checking: Some annotations prevent runtime bugs by enforcing constraints during compilation.

Summary of the Tutorial

Annotations in Java act as metadata that can influence how the compiler or runtime behaves. You can use built-in annotations like @Override, @Deprecated, and @SuppressWarnings for standard use cases, or create custom ones for advanced situations. They’re an essential part of modern Java programming, especially in frameworks like Spring, Hibernate, and JUnit.


Practice Questions

  1. Create a Java program that uses the @Override annotation to override a method in a subclass and print a custom message.

  2. Write a Java class with a method marked as @Deprecated. Call this method from another class and observe the compiler warning.

  3. Write a Java program that uses @SuppressWarnings("unchecked") while working with a raw ArrayList. Explain why this annotation is used.

  4. Create a functional interface Calculator using @FunctionalInterface and implement it with a lambda expression to perform addition.

  5. Write a custom annotation @Developer that has fields name and project. Apply it to a class and print the values using reflection.

  6. Create a custom annotation @Version with a default value of 1.0. Apply it to multiple classes and use reflection to print each class's version.

  7. Define a custom annotation @Todo that contains a message and priority. Apply it to methods that need future improvement.

  8. Write a program that uses @Inherited to show how an annotation on a superclass is inherited by its subclass.

  9. Create a custom annotation @Documentation with @Documented meta-annotation and generate its Javadoc to verify the annotation appears.

  10. Write a program that uses the @Target and @Retention meta-annotations to restrict a custom annotation to methods and make it available at runtime.


Try a Short Quiz.

coding learning websites codepractice

No quizzes available.

Go Back Top