Table of contents
1.
Introduction
2.
Marker Annotations
3.
Single Value Annotations
4.
Full Annotations
5.
Type Annotations
6.
Repeating Annotations
7.
Predefined/Standard Annotations
7.1.
@Override 
7.2.
@Deprecated 
7.3.
@SuppressWarnings
7.4.
@FunctionalInterface 
8.
Description & Example of Specific Annotations
8.1.
@Documented
8.2.
@Target Annotation
8.3.
@Inherited Annotation
9.
User-defined (Custom) Annotations
10.
Frequently Asked Questions
10.1.
What are Java annotations used for?
10.2.
Can annotations affect the execution of a program?
10.3.
How do you choose which type of annotation to use?
11.
Conclusion
Last Updated: Jun 2, 2024
Easy

Java Annotations

Author Riya Singh
0 upvote
Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

Java annotations are a form of metadata that you can add to your code. They help to provide additional information about your program but do not change how your code runs. 

Java Annotations

This article explains what Java annotations are, their significance, and how you can use them in your coding projects. We will look at different types of annotations and see examples of how they are applied in real Java programs. 

Marker Annotations

Marker annotations in Java are the simplest form of annotations, with no methods. They are used primarily to signal the presence of a property or behavior to other parts of a program or tools. For example, you might use a marker annotation to indicate that a method is a test method in unit testing frameworks like JUnit.

Here’s how you can define a marker annotation:

public @interface MyMarker {
}


And applying it to a method would look like this:

@MyMarker
public void showSomething() {
    System.out.println("This method is marked.");
}


Note : Marker annotations are useful because they make the code easier to read & manage by signaling specific behaviors or properties without needing additional information.

Single Value Annotations

Single value annotations in Java allow you to include one piece of information when you use the annotation. They are designed to store a single value, making them ideal for settings that require only one parameter. This type of annotation is handy when you need to pass a specific value to your annotated element without creating a full-fledged attribute set.

Example 

public @interface ReviewRating {
    int value();  // Single value needed for this annotation
}


You can use this annotation on a class or method like this:



@ReviewRating(5)
public class Product {
    // This class has been given a review rating of 5
}


Note : This approach is straightforward and focuses the reader’s attention on the critical piece of information being conveyed through the annotation, simplifying the code’s metadata aspect.

Full Annotations

Full annotations in Java are more detailed than marker or single value annotations because they can include multiple values. This type of annotation is beneficial when you need to provide several pieces of information about the annotation context or environment. Each value in a full annotation is defined as a 'method' within the annotation.

Example 

public @interface TaskDetails {
    String author();
    String date();
    int currentVersion() default 1;
}


In the example above, TaskDetails can store information about the task's author, date, and version. You can provide defaults for some values, like the currentVersion, which simplifies usage when the common case applies.

To use this annotation, you would write:

@TaskDetails(author = "Rahul Singh", date = "2024-05-28")
public void myTask() {
    System.out.println("This task is described with multiple details.");
}


Note : This kind of annotation is perfect for cases where detailed metadata is crucial for the functionality or tracking of elements within your Java applications, enhancing maintainability and readability.

Type Annotations

Type annotations in Java extend the capabilities of annotations by allowing them to be used anywhere you use a type. This includes new objects, type casts, implements clauses, and more. They are particularly useful for improving type checking at compile time and can help avoid errors by providing extra information about the usage of types within your code.

Example : 

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target(ElementType.TYPE_USE) // This specifies where the annotation can be applied.
public @interface NonEmpty {
}

public class Container<@NonEmpty T> { // This annotation is used on a type parameter.
    private T value;
}
public void processItems() {
    Container<@NonEmpty String> stringContainer = new Container<>();
}


In the example above, @NonEmpty is a type annotation that can be applied to type usages. It can be used to ensure that certain containers should not be empty, providing a compile-time check that can prevent runtime errors.

Note : Type annotations add a layer of safety and clarity to Java programming by allowing developers to specify more details about how types are intended to be used.

Repeating Annotations

Repeating annotations in Java allow you to apply the same annotation to a declaration or type use more than once. Before Java 8, to apply the same annotation multiple times, you had to wrap them in a container annotation. Repeating annotations streamline this process by enabling you to directly repeat an annotation without needing a container.

Example : 

import java.lang.annotation.Repeatable;


// Define a container for the annotation
public @interface Alerts {
    Alert[] value();
}


// Define the repeatable annotation
@Repeatable(Alerts.class)
public @interface Alert {
    String message();
}

// Applying multiple Alert annotations
public class Server {
    @Alert(message = "Low disk space")
    @Alert(message = "Memory usage high")
    public void checkSystem() {
        System.out.println("System checks running...");
    }
}


In this example, Alert is the repeatable annotation, and Alerts is the container that holds multiple Alert annotations. This approach is very helpful when you need to declare multiple instances of the same type of information, like warnings or notes on a method or class, making the code cleaner and more intuitive to read.

Note : Repeating annotations provide a flexible way to convey repeated metadata information clearly and succinctly.

Predefined/Standard Annotations

Java provides several predefined or standard annotations that you can use to inform the compiler about certain behaviors or to enforce certain properties. These annotations are built into the Java Development Kit (JDK) and offer a range of functionalities that help with various aspects of programming, from suppressing warnings to overriding methods.

Here are a few key standard annotations in Java:

@Override 

This annotation indicates that a method is intended to override a method declared in a superclass. For example, if you have a method in a child class that should replace a method in the parent class, you use this annotation to tell the compiler that is your intention. It helps catch errors where a method in the child class might not correctly override the parent class method.

Example: 

public class Animal {
    public void displayInfo() {
        System.out.println("I am an animal.");
    }
}
public class Dog extends Animal {
    @Override
    public void displayInfo() {
        System.out.println("I am a dog.");
    }
}

@Deprecated 

This annotation is used to mark a Java API element that is no longer recommended to use. The use of the @Deprecated annotation indicates that the marked element might be removed in future versions. It's a way of informing users that they should look for or use an alternative.

Example: 

public class Utility {
    @Deprecated
    public void showDate() {
        System.out.println("Display date.");
    }
}

@SuppressWarnings

This annotation is used to suppress compiler warnings for a particular part of the program. It is useful when you know that certain warnings can be safely ignored. You specify the type of warnings that you want to suppress as a string within the annotation.

Example : 

@SuppressWarnings("unchecked")
public void processList() {
    List rawList = new ArrayList();
    rawList.add("test");
    System.out.println("List processed.");
}

@FunctionalInterface 

This annotation indicates that the type declaration is intended to be a functional interface, as defined by the Java Language Specification. Functional interfaces are those that contain exactly one abstract method, and they are often used as the basis for lambda expressions in Java 8 and beyond.

Example : 

@FunctionalInterface
public interface SimpleFuncInterface {
    void doWork();
}


Note : All these annotations makes the intentions behind your code clearer, not only to the compiler but to anyone else reading your code. This clarity can prevent errors and improve code maintainability.

Description & Example of Specific Annotations

@Documented

The @Documented annotation is used to indicate that an annotation should be documented in the JavaDoc tool. This means when you use the @Documented annotation on your custom annotations, their presence will be noted in the API documentation, making it easier for other developers to understand their use.

Here is how you define and use the @Documented annotation:

import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Info {
    String description();
}
@Info(description = "This method calculates the sum of two numbers.")
public int add(int a, int b) {
    return a + b;
}


In this example, @Info is an annotation that we've created, which includes a description of what the method does. By marking @Info with @Documented, we ensure that its use in add(int a, int b) will appear in JavaDocs, providing clear documentation for anyone who uses or references this method.

@Target Annotation

The @Target annotation specifies where your custom annotation can be used in the Java code. It helps prevent misuse by restricting the application of an annotation to Java elements like methods, fields, classes, etc. By defining the scope, @Target ensures annotations are only used where they make logical sense.

Here's how you define and use the @Target annotation:

import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AccessLevel {
    String level() default "guest";
}
public class Security {
    @AccessLevel(level = "admin")
    public void configureSettings() {
        System.out.println("Configuring system settings...");
    }
    @AccessLevel // Uses default access level "guest"
    public void viewSettings() {
        System.out.println("Viewing system settings...");
    }
}


In this example, @AccessLevel is used to specify access levels for different parts of an application. By setting @Target to ElementType.METHOD and ElementType.FIELD, the annotation ensures that it can only be applied to methods or fields, which makes it clear and prevents it from being mistakenly placed on a class or package.

@Inherited Annotation

The @Inherited annotation in Java signals that an annotation type is automatically inherited by subclasses. When you use @Inherited, if a class is annotated with a particular annotation, all subclasses of that class automatically inherit that annotation too, unless they are explicitly annotated with a different instance of the same annotation.

Here’s how you can define and use the @Inherited annotation:

import java.lang.annotation.*;

@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface Configuration {
    String setting();
}
@Configuration(setting = "Optimized")
public class SystemConfig {
}
public class ExtendedSystemConfig extends SystemConfig {
    // Inherits the Configuration annotation with setting "Optimized"
}


In this example, the @Configuration annotation is marked with @Inherited. Therefore, ExtendedSystemConfig, which is a subclass of SystemConfig, automatically inherits the @Configuration annotation. This is particularly useful for behavior that should be consistent across a hierarchy of classes, such as configuration settings or security policies.

User-defined (Custom) Annotations

User-defined or custom annotations in Java allow developers to create their own annotations that are tailored to specific requirements of their applications. These annotations can provide a structured way to include metadata in the code, which can later be used for processing by compilers, development tools, or runtime libraries.

Here’s how you can define and use a custom annotation:

import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Action {
    String description();
}
public class TaskProcessor {
    @Action(description = "Saves the current session state.")
    public void saveSession() {
        System.out.println("Session saved.");
    }
    @Action(description = "Loads a previously saved session.")
    public void loadSession() {
        System.out.println("Session loaded.");
    }
}


In this example, the @Action annotation is a custom annotation created to describe the actions performed by methods in a TaskProcessor class. By using this annotation, developers can quickly understand what each method does, enhancing code readability and maintainability. The description attribute of the @Action annotation provides a clear, human-readable description of what the method is intended to do, which is particularly helpful during code reviews or when maintaining old code.

Note : Custom annotations like @Action are powerful tools in Java because they allow you to define how information is stored in a programmatic and standardized way, making your applications easier to manage and extend.

Frequently Asked Questions

What are Java annotations used for?

Java annotations are used to provide metadata about the code. This metadata can be processed by the compiler, development tools, or at runtime to perform certain actions, enforce rules, or influence the behavior of applications.

Can annotations affect the execution of a program?

Annotations themselves do not directly affect the execution of a program unless they are designed to be used by other tools or frameworks that influence runtime behavior, like Spring or Hibernate.

How do you choose which type of annotation to use?

The choice depends on what you need to accomplish. Use marker annotations for simple flagging, single value annotations for passing a single data element, and full annotations when multiple properties need to be set. Custom annotations are best when you need specific behavior that pre-built Java annotations cannot provide.

Conclusion

In this article, we have learned about Java annotations, which includes their types such as marker, single value, full, and type annotations. We also discussed about repeating annotations and explained predefined annotations like @Deprecated, @Override, and @SuppressWarnings. Apart from these we also talked about the @Documented, @Target, @Inherited annotations and saw how to define and use custom annotations. Annotations are a powerful part of Java, offering a means to add metadata that can guide how applications are built, tested, and run. 

You can refer to our guided paths on the Coding Ninjas. You can check our course to learn more about DSADBMSCompetitive ProgrammingPythonJavaJavaScript, etc. Also, check out some of the Guided Paths on topics such as Data Structure andAlgorithmsCompetitive ProgrammingOperating SystemsComputer Networks, DBMSSystem Design, etc., as well as some Contests, Test Series, and Interview Experiences curated by top Industry.

Live masterclass