Practical Examples of Java Annotations

Explore practical examples of Java annotations to enhance code clarity and functionality.
By Jamie

Understanding Java Annotations

Java annotations are special markers in the code that provide metadata about the program. They can influence the behavior of the program at runtime or compile time, making them a valuable tool for developers. This article presents practical examples of Java annotations that demonstrate their functionality and applications.

Example 1: Custom Annotation for Logging

Use Case

In many applications, logging is crucial for debugging and monitoring. Creating a custom annotation for logging can help streamline this process across different methods.

import java.lang.annotation.*;
import java.lang.reflect.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface LogExecution {
}

class Logger {
    @LogExecution
    public void process() {
        System.out.println("Processing...");
    }

    public void execute() throws Exception {
        for (Method method : this.getClass().getDeclaredMethods()) {
            if (method.isAnnotationPresent(LogExecution.class)) {
                System.out.println("Executing: " + method.getName());
                method.invoke(this);
            }
        }
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        Logger logger = new Logger();
        logger.execute();
    }
}

Notes

  • The @Retention(RetentionPolicy.RUNTIME) annotation makes our custom annotation available at runtime, allowing reflection to be used.
  • Use this logging annotation to automatically log method executions, improving code maintainability.

Example 2: Built-in Annotations for Overriding Methods

Use Case

The @Override annotation is a built-in annotation that helps clarify when a method is intended to override a method in a superclass. This practice prevents errors and enhances code readability.

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

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

public class Main {
    public static void main(String[] args) {
        Animal myDog = new Dog();
        myDog.sound(); // Output: Dog barks
    }
}

Notes

  • If the method in the subclass doesn’t match the method signature in the superclass, the compiler will throw an error, helping prevent potential bugs.
  • This annotation is particularly useful in large codebases where method names can be duplicated.

Example 3: Annotations for Dependency Injection

Use Case

Frameworks like Spring use annotations to manage dependencies and configuration. The @Autowired annotation is used to automatically inject dependencies into classes.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
class Service {
    public void serve() {
        System.out.println("Service is serving...");
    }
}

@Component
class Consumer {
    @Autowired
    private Service service;

    public void doSomething() {
        service.serve();
    }
}

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        Consumer consumer = context.getBean(Consumer.class);
        consumer.doSomething();
    }
}

Notes

  • The @Autowired annotation tells Spring to automatically inject the Service bean into the Consumer class, promoting loose coupling.
  • This example requires a Spring context to run, showcasing how annotations can simplify dependency management in applications.

These examples of Java annotations illustrate how they can improve code clarity, streamline processes, and enhance functionality in various scenarios. Understanding and applying these annotations can greatly benefit developers in their programming endeavors.