Annotations in Java
Question
Explain the concept of annotations in Java. How do you create custom annotations, and what are their various uses? What are the best practices for working with annotations?
Answer
Annotations in Java provide metadata about code elements. They can be used for documentation, compilation instructions, and runtime processing. Annotations are a powerful feature that enables declarative programming and reduces boilerplate code.
Built-in Annotations
-
Documentation Annotations
/** * @author John Doe * @version 1.0 */ @Deprecated public class OldClass { @Override public String toString() { return "Old class"; } }
-
Compilation Annotations
@SuppressWarnings("unchecked") public class WarningExample { @FunctionalInterface public interface MyFunction { void execute(); } }
Creating Custom Annotations
-
Basic Annotation
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) public @interface Log { String value() default ""; Level level() default Level.INFO; } public enum Level { INFO, WARNING, ERROR } // Usage @Log(level = Level.INFO) public class Service { @Log("Processing request") public void processRequest() { // Method implementation } }
-
Annotation with Multiple Parameters
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Validate { int min() default 0; int max() default Integer.MAX_VALUE; String pattern() default ""; String message() default "Validation failed"; } // Usage public class UserService { @Validate(min = 18, max = 100, message = "Age must be between 18 and 100") public void setAge(int age) { // Method implementation } }
Annotation Processing
-
Runtime Annotation Processing
public class LogProcessor { public static void processAnnotations(Object obj) { Class<?> clazz = obj.getClass(); // Process class-level annotations Log classLog = clazz.getAnnotation(Log.class); if (classLog != null) { System.out.println("Class log level: " + classLog.level()); } // Process method-level annotations for (Method method : clazz.getDeclaredMethods()) { Log methodLog = method.getAnnotation(Log.class); if (methodLog != null) { System.out.println("Method: " + method.getName() + ", Log message: " + methodLog.value()); } } } }
-
Compile-time Annotation Processing
@SupportedAnnotationTypes("com.example.Validate") @SupportedSourceVersion(SourceVersion.RELEASE_11) public class ValidationProcessor extends AbstractProcessor { @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { for (Element element : roundEnv.getElementsAnnotatedWith(Validate.class)) { Validate validate = element.getAnnotation(Validate.class); // Generate validation code } return true; } }
Common Use Cases
-
Dependency Injection
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Autowired { boolean required() default true; } public class UserService { @Autowired private UserRepository userRepository; @Autowired(required = false) private CacheService cacheService; }
-
Unit Testing
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Test { String description() default ""; Class<? extends Throwable> expected() default None.class; } public class UserTest { @Test(description = "Should create user successfully") public void testCreateUser() { // Test implementation } @Test(expected = IllegalArgumentException.class) public void testInvalidUser() { // Test implementation } }
Best Practices
-
Annotation Design
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Configuration { // Use primitive types or String for values String[] basePackages() default {}; // Use enums for fixed set of values Scope scope() default Scope.SINGLETON; // Use Class for type references Class<?>[] exclude() default {}; } public enum Scope { SINGLETON, PROTOTYPE }
-
Annotation Documentation
/** * Indicates that a method is a test case. * This annotation can be used to mark methods that should be executed * as part of the test suite. * * @see TestRunner */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Test { /** * The description of the test case. * This will be displayed in test reports. */ String description() default ""; /** * The expected exception that should be thrown. * If no exception is expected, use None.class. */ Class<? extends Throwable> expected() default None.class; }
Common Use Cases
-
Validation Framework
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.PARAMETER}) public @interface NotNull { String message() default "Value cannot be null"; } @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.PARAMETER}) public @interface Size { int min() default 0; int max() default Integer.MAX_VALUE; String message() default "Size must be between {min} and {max}"; } public class User { @NotNull(message = "Name is required") private String name; @Size(min = 3, max = 50) private String email; }
-
ORM Mapping
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Entity { String table() default ""; } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Column { String name() default ""; boolean nullable() default true; } @Entity(table = "users") public class User { @Column(name = "user_id", nullable = false) private Long id; @Column(name = "user_name") private String name; }
Testing
- Testing Annotation Processing
@Test public void testLogAnnotation() { Service service = new Service(); LogProcessor.processAnnotations(service); // Verify log output } @Test public void testValidationAnnotation() { User user = new User(); ValidationProcessor processor = new ValidationProcessor(); // Test validation logic }
Common Pitfalls
-
Annotation Retention
// Bad - Missing retention policy public @interface BadAnnotation { String value(); } // Good - Explicit retention policy @Retention(RetentionPolicy.RUNTIME) public @interface GoodAnnotation { String value(); }
-
Annotation Target
// Bad - Missing target @Retention(RetentionPolicy.RUNTIME) public @interface BadAnnotation { String value(); } // Good - Explicit target @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface GoodAnnotation { String value(); }