Custom annotations in Java allow developers to define metadata for classes, methods, fields, or other program elements. This metadata can be processed at compile-time or runtime to enhance functionality or enforce specific behaviors. This article demonstrates how to create and process custom annotations in Advanced Java.
To define a custom annotation, use the @interface keyword. Annotations can include elements (attributes) with default values.
Example:
import java.lang.annotation.*; // Define a custom annotation @Retention(RetentionPolicy.RUNTIME) // Make annotation available at runtime @Target(ElementType.METHOD) // Restrict usage to methods public @interface MyAnnotation { String value() default "Default Value"; // Annotation element with default int priority() default 1; // Another element }
Use the custom annotation on methods, fields, or classes as specified in the @Target.
Example:
public class MyClass { @MyAnnotation(value = "Custom Annotation Example", priority = 5) public void annotatedMethod() { System.out.println("This method is annotated with @MyAnnotation."); } @MyAnnotation // Using default values public void anotherAnnotatedMethod() { System.out.println("This method uses default annotation values."); } }
Use reflection to process the custom annotation at runtime. This involves inspecting classes, methods, or fields for the annotation and retrieving its values.
Example:
import java.lang.reflect.Method; public class AnnotationProcessor { public static void main(String[] args) { try { // Get the Class object Class> clazz = MyClass.class; // Loop through all methods for (Method method : clazz.getDeclaredMethods()) { // Check if the method has @MyAnnotation if (method.isAnnotationPresent(MyAnnotation.class)) { // Get the annotation MyAnnotation annotation = method.getAnnotation(MyAnnotation.class); // Print annotation details System.out.println("Method: " + method.getName()); System.out.println("Value: " + annotation.value()); System.out.println("Priority: " + annotation.priority()); } } } catch (Exception e) { e.printStackTrace(); } } }
Custom annotations can be used for more advanced tasks such as enforcing validation rules, injecting dependencies, or controlling behavior.
Create an annotation for validating input fields.
import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface NotNull { } class User { @NotNull private String name; public User(String name) { this.name = name; } public String getName() { return name; } } public class ValidationProcessor { public static void main(String[] args) { try { User user = new User(null); // Invalid user Class> clazz = user.getClass(); for (var field : clazz.getDeclaredFields()) { if (field.isAnnotationPresent(NotNull.class)) { field.setAccessible(true); Object value = field.get(user); if (value == null) { System.out.println("Field " + field.getName() + " cannot be null!"); } } } } catch (Exception e) { e.printStackTrace(); } } }
Implement a custom annotation to log method execution.
import java.lang.annotation.*; import java.lang.reflect.*; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface LogExecution { } class Service { @LogExecution public void performTask() { System.out.println("Task performed!"); } } public class LoggingProcessor { public static void main(String[] args) { try { Service service = new Service(); Method[] methods = service.getClass().getDeclaredMethods(); for (Method method : methods) { if (method.isAnnotationPresent(LogExecution.class)) { System.out.println("Executing method: " + method.getName()); method.invoke(service); // Invoke the method } } } catch (Exception e) { e.printStackTrace(); } } }
Custom annotations in Java provide a powerful mechanism for adding metadata to code and enabling dynamic behaviors at runtime. By understanding how to define, apply, and process annotations, developers can create flexible and reusable solutions.