Annotations in Java

Table of Contents

  1. Introduction to Annotations
  2. Built-in Annotations
  3. Custom Annotations
  4. Annotation Retention
  5. Annotation Targets
  6. Annotation Parameters
  7. Repeatable Annotations
  8. Annotation Processing
  9. Best Practices
  10. Complete Examples

Introduction to Annotations

Annotations are metadata that provide data about a program but are not part of the program itself. They have no direct effect on the operation of the code they annotate.

Basic Syntax:

@AnnotationName
public class MyClass {
@AnnotationName
private String field;
@AnnotationName
public void method() {
// method body
}
}

Simple Example:

// Using built-in annotation
@Deprecated
public class OldClass {
@SuppressWarnings("unchecked")
public void oldMethod() {
List list = new ArrayList(); // Raw type - generates warning
list.add("test");
}
}
public class AnnotationDemo {
public static void main(String[] args) {
OldClass old = new OldClass();
old.oldMethod();
}
}

Built-in Annotations

1. @Override:

public class OverrideExample {
static class Animal {
public void makeSound() {
System.out.println("Animal makes a sound");
}
public void eat() {
System.out.println("Animal is eating");
}
}
static class Dog extends Animal {
// ✅ Correct - overrides parent method
@Override
public void makeSound() {
System.out.println("Dog barks");
}
// ❌ Compile-time error - method doesn't exist in parent
// @Override
// public void makeNoise() { // This would cause error
//     System.out.println("Dog makes noise");
// }
// No @Override - but if parent method changes, no error
public void eat() {
System.out.println("Dog is eating");
}
}
public static void main(String[] args) {
Dog dog = new Dog();
dog.makeSound(); // Dog barks
dog.eat();       // Dog is eating
}
}

2. @Deprecated:

public class DeprecatedExample {
/**
* Old way of greeting - use {@link #newGreet(String)} instead
* @deprecated This method is deprecated because it uses old formatting
*/
@Deprecated(since = "2.0", forRemoval = true)
public static void oldGreet(String name) {
System.out.println("Hello, " + name + "!");
}
/**
* New way of greeting with better formatting
*/
public static void newGreet(String name) {
System.out.println("🌟 Hello, " + name + "! 🌟");
}
public static void main(String[] args) {
// This will show warning in IDE
oldGreet("Alice"); // Deprecated method call
// Recommended way
newGreet("Bob");
// Using reflection to check annotation
try {
Method oldMethod = DeprecatedExample.class.getMethod("oldGreet", String.class);
Deprecated deprecated = oldMethod.getAnnotation(Deprecated.class);
if (deprecated != null) {
System.out.println("oldGreet is deprecated since: " + deprecated.since());
System.out.println("Scheduled for removal: " + deprecated.forRemoval());
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}

3. @SuppressWarnings:

import java.util.*;
public class SuppressWarningsExample {
// Suppress unchecked warnings for this entire class
@SuppressWarnings("unchecked")
public void processRawList() {
List rawList = new ArrayList(); // Raw type usage
rawList.add("String");
rawList.add(123); // Mixing types
String str = (String) rawList.get(0); // Unchecked cast
}
public void methodWithMultipleWarnings() {
// Suppress multiple warnings
@SuppressWarnings({"rawtypes", "unchecked"})
List list = new ArrayList();
list.add("test");
// Suppress deprecation warning
@SuppressWarnings("deprecation")
Date date = new Date(2020, 1, 1); // Deprecated constructor
}
// Common warning types:
// - "unchecked" - for generic type safety
// - "deprecation" - for use of deprecated items
// - "rawtypes" - for use of raw types
// - "unused" - for unused variables
// - "all" - for all warnings
public static void main(String[] args) {
SuppressWarningsExample example = new SuppressWarningsExample();
example.processRawList();
example.methodWithMultipleWarnings();
}
}

4. @FunctionalInterface:

public class FunctionalInterfaceExample {
// Valid functional interface - has exactly one abstract method
@FunctionalInterface
interface Calculator {
int calculate(int a, int b);
// Can have default methods
default void printResult(int result) {
System.out.println("Result: " + result);
}
// Can have static methods
static String getOperationName() {
return "Calculation";
}
}
// ❌ Invalid - will cause compile error
// @FunctionalInterface
// interface InvalidFunctional {
//     void method1();
//     void method2(); // More than one abstract method
// }
public static void main(String[] args) {
// Using lambda with functional interface
Calculator adder = (a, b) -> a + b;
Calculator multiplier = (a, b) -> a * b;
System.out.println("Addition: " + adder.calculate(5, 3));
System.out.println("Multiplication: " + multiplier.calculate(5, 3));
adder.printResult(adder.calculate(10, 20));
System.out.println("Operation: " + Calculator.getOperationName());
}
}

Custom Annotations

Creating Custom Annotations:

import java.lang.annotation.*;
import java.lang.reflect.*;
// Marker annotation (no parameters)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Development {
// No elements - marker annotation
}
// Single-value annotation
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@interface Author {
String value(); // Special case - can use without name when only one element
}
// Multi-value annotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface TestCase {
int id();
String description();
String[] tags() default {}; // Optional element with default value
boolean enabled() default true;
}
// Using the custom annotations
@Development
@Author("John Doe")
class PaymentProcessor {
@TestCase(
id = 1,
description = "Test successful payment",
tags = {"payment", "success"},
enabled = true
)
public void processPayment(double amount) {
System.out.println("Processing payment: $" + amount);
}
@TestCase(
id = 2,
description = "Test failed payment",
tags = {"payment", "failure"},
enabled = false // This test is disabled
)
public void processFailedPayment(double amount) {
System.out.println("Processing failed payment: $" + amount);
}
@Author("Jane Smith")
public void refundPayment(double amount) {
System.out.println("Refunding payment: $" + amount);
}
}
public class CustomAnnotationDemo {
public static void main(String[] args) {
// Process annotations using reflection
Class<PaymentProcessor> processorClass = PaymentProcessor.class;
// Check class-level annotations
if (processorClass.isAnnotationPresent(Development.class)) {
System.out.println("PaymentProcessor is in development");
}
Author classAuthor = processorClass.getAnnotation(Author.class);
if (classAuthor != null) {
System.out.println("Class author: " + classAuthor.value());
}
// Process method annotations
for (Method method : processorClass.getDeclaredMethods()) {
System.out.println("\nMethod: " + method.getName());
// Check TestCase annotation
TestCase testCase = method.getAnnotation(TestCase.class);
if (testCase != null) {
System.out.println("  Test ID: " + testCase.id());
System.out.println("  Description: " + testCase.description());
System.out.println("  Enabled: " + testCase.enabled());
System.out.println("  Tags: " + String.join(", ", testCase.tags()));
}
// Check Author annotation
Author author = method.getAnnotation(Author.class);
if (author != null) {
System.out.println("  Author: " + author.value());
}
}
}
}

Output:

PaymentProcessor is in development
Class author: John Doe
Method: processPayment
Test ID: 1
Description: Test successful payment
Enabled: true
Tags: payment, success
Method: processFailedPayment
Test ID: 2
Description: Test failed payment
Enabled: false
Tags: payment, failure
Method: refundPayment
Author: Jane Smith

Annotation Retention

Retention Policies:

import java.lang.annotation.*;
// SOURCE - discarded by compiler, not available at runtime
@Retention(RetentionPolicy.SOURCE)
@interface SourceLevelAnnotation {
String value();
}
// CLASS - stored in .class file but not available at runtime (default)
@Retention(RetentionPolicy.CLASS)
@interface ClassLevelAnnotation {
String value();
}
// RUNTIME - stored in .class file and available at runtime
@Retention(RetentionPolicy.RUNTIME)
@interface RuntimeLevelAnnotation {
String value();
}
@SourceLevelAnnotation("This won't be available at runtime")
@ClassLevelAnnotation("This might be available depending on JVM")
@RuntimeLevelAnnotation("This will be available at runtime")
class RetentionDemo {
@SourceLevelAnnotation("source method")
@ClassLevelAnnotation("class method") 
@RuntimeLevelAnnotation("runtime method")
public void demoMethod() {
System.out.println("Method demonstration");
}
}
public class RetentionExample {
public static void main(String[] args) {
Class<RetentionDemo> demoClass = RetentionDemo.class;
// Check runtime annotations
System.out.println("=== Runtime Annotations ===");
RuntimeLevelAnnotation runtime = demoClass.getAnnotation(RuntimeLevelAnnotation.class);
if (runtime != null) {
System.out.println("Class Runtime: " + runtime.value());
}
// Check class-level annotations (may not be available)
System.out.println("\n=== Class-level Annotations ===");
ClassLevelAnnotation classLevel = demoClass.getAnnotation(ClassLevelAnnotation.class);
if (classLevel != null) {
System.out.println("Class Class-level: " + classLevel.value());
} else {
System.out.println("Class-level annotation not available at runtime");
}
// Check source-level annotations (won't be available)
System.out.println("\n=== Source-level Annotations ===");
SourceLevelAnnotation source = demoClass.getAnnotation(SourceLevelAnnotation.class);
if (source != null) {
System.out.println("Class Source: " + source.value());
} else {
System.out.println("Source-level annotation not available at runtime");
}
// Check method annotations
try {
Method method = demoClass.getMethod("demoMethod");
System.out.println("\n=== Method Annotations ===");
RuntimeLevelAnnotation methodRuntime = method.getAnnotation(RuntimeLevelAnnotation.class);
if (methodRuntime != null) {
System.out.println("Method Runtime: " + methodRuntime.value());
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}

Output:

=== Runtime Annotations ===
Class Runtime: This will be available at runtime
=== Class-level Annotations ===
Class-level annotation not available at runtime
=== Source-level Annotations ===
Source-level annotation not available at runtime
=== Method Annotations ===
Method Runtime: runtime method

Annotation Targets

Target Elements:

import java.lang.annotation.*;
// Annotation that can be applied to different elements
@Target({
ElementType.TYPE,           // Class, interface, enum
ElementType.FIELD,          // Field (including enum constants)
ElementType.METHOD,         // Method
ElementType.PARAMETER,      // Method parameter
ElementType.CONSTRUCTOR,    // Constructor
ElementType.LOCAL_VARIABLE, // Local variable
ElementType.ANNOTATION_TYPE,// Annotation type
ElementType.PACKAGE,        // Package
ElementType.TYPE_PARAMETER, // Type parameter (Java 8+)
ElementType.TYPE_USE        // Type use (Java 8+)
})
@Retention(RetentionPolicy.RUNTIME)
@interface MultiTarget {
String value();
}
// Specific target examples
@Target(ElementType.TYPE)
@interface ClassInfo {
String author();
String version();
}
@Target(ElementType.METHOD)
@interface Operation {
String name();
boolean transactional() default true;
}
@Target(ElementType.FIELD)
@interface Column {
String name();
boolean nullable() default true;
}
@Target(ElementType.PARAMETER)
@interface NotNull {
String message() default "Parameter cannot be null";
}
@Target(ElementType.TYPE_USE)
@interface NonNull {
// For type checking
}
// Using the targeted annotations
@ClassInfo(author = "Alice", version = "1.0")
class UserService {
@Column(name = "user_id", nullable = false)
private Long userId;
@Column(name = "username")
private String username;
public UserService(@NotNull String initialUser) {
this.username = initialUser;
}
@Operation(name = "createUser", transactional = true)
public void createUser(@NotNull String username, @NotNull String email) {
this.username = username;
System.out.println("Creating user: " + username);
}
@Operation(name = "deleteUser")
public void deleteUser(Long userId) {
System.out.println("Deleting user: " + userId);
}
// Using TYPE_USE annotation
public @NonNull String getUserName() {
return username;
}
public void setUserName(@NonNull String username) {
this.username = username;
}
}
public class TargetExample {
public static void main(String[] args) throws NoSuchMethodException {
Class<UserService> serviceClass = UserService.class;
// Class-level annotation
ClassInfo classInfo = serviceClass.getAnnotation(ClassInfo.class);
if (classInfo != null) {
System.out.println("Class Author: " + classInfo.author());
System.out.println("Class Version: " + classInfo.version());
}
// Method annotations
Method createUserMethod = serviceClass.getMethod("createUser", String.class, String.class);
Operation operation = createUserMethod.getAnnotation(Operation.class);
if (operation != null) {
System.out.println("\nMethod Operation: " + operation.name());
System.out.println("Transactional: " + operation.transactional());
}
// Parameter annotations
Annotation[][] paramAnnotations = createUserMethod.getParameterAnnotations();
for (int i = 0; i < paramAnnotations.length; i++) {
for (Annotation annotation : paramAnnotations[i]) {
if (annotation instanceof NotNull) {
System.out.println("Parameter " + i + " has @NotNull: " + 
((NotNull) annotation).message());
}
}
}
// Field annotations
for (java.lang.reflect.Field field : serviceClass.getDeclaredFields()) {
Column column = field.getAnnotation(Column.class);
if (column != null) {
System.out.println("\nField: " + field.getName());
System.out.println("Column: " + column.name());
System.out.println("Nullable: " + column.nullable());
}
}
}
}

Output:

Class Author: Alice
Class Version: 1.0
Method Operation: createUser
Transactional: true
Parameter 0 has @NotNull: Parameter cannot be null
Parameter 1 has @NotNull: Parameter cannot be null
Field: userId
Column: user_id
Nullable: false
Field: username
Column: username
Nullable: true

Annotation Parameters

Parameter Types and Defaults:

import java.lang.annotation.*;
import java.util.*;
// Annotation with various parameter types
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface TestConfig {
// Primitive types
int timeout() default 30;          // seconds
boolean enabled() default true;
// String
String name();
String description() default "";
// Class
Class<?> expectedException() default None.class;
// Enum
Priority priority() default Priority.MEDIUM;
// Array
String[] tags() default {};
// Annotation
Author author() default @Author("Unknown");
// Special marker for no exception
class None extends Throwable {
private None() {}
}
}
// Enum for priority
enum Priority {
LOW, MEDIUM, HIGH, CRITICAL
}
// Nested annotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Author {
String value();
String email() default "";
}
// Using the annotation with different parameter combinations
class TestConfigurationExample {
@TestConfig(
name = "userCreationTest",
timeout = 60,
priority = Priority.HIGH,
tags = {"user", "creation", "integration"},
author = @Author(value = "Bob", email = "[email protected]")
)
public void testUserCreation() {
System.out.println("Testing user creation...");
}
@TestConfig(
name = "quickTest",
description = "A quick sanity check",
expectedException = IllegalArgumentException.class
)
public void testWithException() {
System.out.println("Testing exception case...");
throw new IllegalArgumentException("Expected exception");
}
@TestConfig(
name = "disabledTest",
enabled = false
)
public void disabledTest() {
System.out.println("This test is disabled");
}
// Using default values for everything
@TestConfig(name = "defaultTest")
public void defaultTest() {
System.out.println("Test with default values");
}
}
public class ParameterExample {
public static void main(String[] args) throws NoSuchMethodException {
Class<TestConfigurationExample> testClass = TestConfigurationExample.class;
// Process each method with @TestConfig annotation
for (Method method : testClass.getDeclaredMethods()) {
TestConfig config = method.getAnnotation(TestConfig.class);
if (config != null) {
System.out.println("\n=== " + method.getName() + " ===");
System.out.println("Name: " + config.name());
System.out.println("Description: " + config.description());
System.out.println("Timeout: " + config.timeout() + "s");
System.out.println("Enabled: " + config.enabled());
System.out.println("Priority: " + config.priority());
System.out.println("Expected Exception: " + config.expectedException().getSimpleName());
System.out.println("Tags: " + Arrays.toString(config.tags()));
Author author = config.author();
System.out.println("Author: " + author.value() + 
(author.email().isEmpty() ? "" : " (" + author.email() + ")"));
// Simulate test execution based on configuration
if (config.enabled()) {
System.out.println("✅ Test would be executed");
} else {
System.out.println("❌ Test is disabled");
}
}
}
}
}

Output:

=== testUserCreation ===
Name: userCreationTest
Description: 
Timeout: 60s
Enabled: true
Priority: HIGH
Expected Exception: None
Tags: [user, creation, integration]
Author: Bob ([email protected])
✅ Test would be executed
=== testWithException ===
Name: quickTest
Description: A quick sanity check
Timeout: 30s
Enabled: true
Priority: MEDIUM
Expected Exception: IllegalArgumentException
Tags: []
Author: Unknown
✅ Test would be executed
=== disabledTest ===
Name: disabledTest
Description: 
Timeout: 30s
Enabled: false
Priority: MEDIUM
Expected Exception: None
Tags: []
Author: Unknown
❌ Test is disabled
=== defaultTest ===
Name: defaultTest
Description: 
Timeout: 30s
Enabled: true
Priority: MEDIUM
Expected Exception: None
Tags: []
Author: Unknown
✅ Test would be executed

Repeatable Annotations

Java 8+ Repeatable Annotations:

import java.lang.annotation.*;
import java.util.*;
// Container annotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Roles {
Role[] value();
}
// Repeatable annotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Repeatable(Roles.class)
@interface Role {
String name();
String[] permissions() default {};
}
// Pre-Java 8 way (without @Repeatable)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface OldRoles {
String[] value();
}
// Using repeatable annotations (Java 8+)
@Role(name = "ADMIN", permissions = {"READ", "WRITE", "DELETE"})
@Role(name = "USER", permissions = {"READ"})
@Role(name = "GUEST", permissions = {"READ_PUBLIC"})
class SecureService {
public void adminOperation() {
System.out.println("Admin operation");
}
public void userOperation() {
System.out.println("User operation");
}
}
// Old way (pre-Java 8)
@OldRoles({"ADMIN", "USER", "GUEST"})
class OldSecureService {
// ...
}
public class RepeatableExample {
public static void main(String[] args) {
Class<SecureService> serviceClass = SecureService.class;
// Get roles using container annotation
Roles rolesContainer = serviceClass.getAnnotation(Roles.class);
if (rolesContainer != null) {
System.out.println("=== Roles using Container ===");
for (Role role : rolesContainer.value()) {
printRole(role);
}
}
// Get roles directly (Java 8+)
System.out.println("\n=== Roles Directly ===");
Role[] rolesDirect = serviceClass.getAnnotationsByType(Role.class);
for (Role role : rolesDirect) {
printRole(role);
}
// Demonstrate the difference
System.out.println("\n=== Annotation Details ===");
Annotation[] annotations = serviceClass.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println("Annotation: " + annotation.annotationType().getSimpleName());
if (annotation instanceof Roles) {
System.out.println("  - This is the container annotation");
} else if (annotation instanceof Role) {
System.out.println("  - This is a repeatable annotation");
}
}
}
private static void printRole(Role role) {
System.out.println("Role: " + role.name());
System.out.println("  Permissions: " + Arrays.toString(role.permissions()));
}
}

Output:

=== Roles using Container ===
Role: ADMIN
Permissions: [READ, WRITE, DELETE]
Role: USER
Permissions: [READ]
Role: GUEST
Permissions: [READ_PUBLIC]
=== Roles Directly ===
Role: ADMIN
Permissions: [READ, WRITE, DELETE]
Role: USER
Permissions: [READ]
Role: GUEST
Permissions: [READ_PUBLIC]
=== Annotation Details ===
Annotation: Roles
- This is the container annotation

Annotation Processing

Runtime Annotation Processing:

import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;
// Custom annotations for validation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface NotNull {
String message() default "Field cannot be null";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Size {
int min() default 0;
int max() default Integer.MAX_VALUE;
String message() default "Size constraint violated";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Email {
String message() default "Invalid email format";
}
// Validation processor
class Validator {
public static List<String> validate(Object obj) {
List<String> errors = new ArrayList<>();
Class<?> clazz = obj.getClass();
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
try {
Object value = field.get(obj);
// Check @NotNull
if (field.isAnnotationPresent(NotNull.class)) {
NotNull notNull = field.getAnnotation(NotNull.class);
if (value == null) {
errors.add(field.getName() + ": " + notNull.message());
}
}
// Check @Size
if (field.isAnnotationPresent(Size.class) && value != null) {
Size size = field.getAnnotation(Size.class);
int length = value.toString().length();
if (length < size.min() || length > size.max()) {
errors.add(field.getName() + ": " + size.message() + 
" (min: " + size.min() + ", max: " + size.max() + 
", actual: " + length + ")");
}
}
// Check @Email
if (field.isAnnotationPresent(Email.class) && value != null) {
String email = value.toString();
if (!isValidEmail(email)) {
Email emailAnnotation = field.getAnnotation(Email.class);
errors.add(field.getName() + ": " + emailAnnotation.message() + 
" (" + email + ")");
}
}
} catch (IllegalAccessException e) {
errors.add("Cannot access field: " + field.getName());
}
}
return errors;
}
private static boolean isValidEmail(String email) {
return email != null && email.matches("^[A-Za-z0-9+_.-]+@(.+)$");
}
}
// Class to validate
class User {
@NotNull(message = "Username is required")
@Size(min = 3, max = 20, message = "Username must be 3-20 characters")
private String username;
@NotNull
@Email
private String email;
@Size(min = 8, message = "Password must be at least 8 characters")
private String password;
private String optionalField;
public User(String username, String email, String password) {
this.username = username;
this.email = email;
this.password = password;
}
// Getters and setters
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
public class AnnotationProcessingExample {
public static void main(String[] args) {
// Test cases
User[] testUsers = {
new User("jo", "invalid-email", "short"), // Multiple errors
new User(null, "[email protected]", "validpassword123"), // Null username
new User("validuser", "invalid", "validpassword123"), // Invalid email
new User("validuser", "[email protected]", "validpassword123") // Valid
};
for (int i = 0; i < testUsers.length; i++) {
System.out.println("\n=== Test User " + (i + 1) + " ===");
System.out.println("Username: " + testUsers[i].getUsername());
System.out.println("Email: " + testUsers[i].getEmail());
System.out.println("Password: " + testUsers[i].getPassword());
List<String> errors = Validator.validate(testUsers[i]);
if (errors.isEmpty()) {
System.out.println("✅ Validation passed!");
} else {
System.out.println("❌ Validation errors:");
errors.forEach(error -> System.out.println("  - " + error));
}
}
// Demonstrate reflection with annotations
System.out.println("\n=== Reflection Analysis ===");
Class<User> userClass = User.class;
for (Field field : userClass.getDeclaredFields()) {
System.out.println("\nField: " + field.getName());
System.out.println("  Type: " + field.getType().getSimpleName());
Annotation[] annotations = field.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println("  Annotation: @" + annotation.annotationType().getSimpleName());
}
}
}
}

Output:

=== Test User 1 ===
Username: jo
Email: invalid-email
Password: short
❌ Validation errors:
- username: Username must be 3-20 characters (min: 3, max: 20, actual: 2)
- email: Invalid email format (invalid-email)
- password: Size constraint violated (min: 8, max: 2147483647, actual: 5)
=== Test User 2 ===
Username: null
Email: [email protected]
Password: validpassword123
❌ Validation errors:
- username: Username is required
=== Test User 3 ===
Username: validuser
Email: invalid
Password: validpassword123
❌ Validation errors:
- email: Invalid email format (invalid)
=== Test User 4 ===
Username: validuser
Email: [email protected]
Password: validpassword123
✅ Validation passed!
=== Reflection Analysis ===
Field: username
Type: String
Annotation: @NotNull
Annotation: @Size
Field: email
Type: String
Annotation: @NotNull
Annotation: @Email
Field: password
Type: String
Annotation: @Size
Field: optionalField
Type: String

Best Practices

1. Use Meaningful Names:

// ✅ Good - clear and descriptive
@interface CacheConfig {
int duration();
TimeUnit unit();
boolean refreshable();
}
// ❌ Avoid - vague names
@interface Config {
int value();
String data();
}

2. Provide Sensible Defaults:

// ✅ Good - sensible defaults
@interface ApiEndpoint {
String path();
String method() default "GET";
boolean secure() default true;
int timeout() default 30;
}
// ❌ Avoid - no defaults when they make sense
@interface BadEndpoint {
String path();
String method(); // Should have default
boolean secure(); // Should have default
}

3. Use Enums for Fixed Values:

// ✅ Good - using enums
enum HttpMethod { GET, POST, PUT, DELETE, PATCH }
@interface RequestMapping {
String path();
HttpMethod method() default HttpMethod.GET;
}
// ❌ Avoid - using strings for fixed values
@interface BadMapping {
String path();
String method(); // Could be any string
}

4. Document Your Annotations:

/**
* Marks a method as an API endpoint that can be called remotely
* 
* @author Developer
* @version 1.0
* @since 2024
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface ApiMethod {
/**
* The HTTP path for this endpoint
* @return the endpoint path
*/
String value();
/**
* HTTP method to use
* @return the HTTP method
*/
HttpMethod method() default HttpMethod.GET;
/**
* Whether authentication is required
* @return true if authentication is required
*/
boolean requiresAuth() default true;
}

Complete Examples

Complete Framework Example:

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

// Framework annotations
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Service {
String name() default "";
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Command {
String value();
String description() default "";
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Inject {
String value() default "";
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Scheduled {
long fixedRate() default -1;
String cron() default "";
}

// Service classes
@Service(name = "userService")
class UserService {

@Command("createUser")
public String createUser(String username, String email) {
return "User created: " + username + " (" + email + ")";
}
@Command("deleteUser")
public String deleteUser(String username) {
return "User deleted: " + username;
}
@Scheduled(fixedRate = 5000)
public void cleanupInactiveUsers() {
System.out.println("Cleaning up inactive users...");
}

}

@Service(name = "emailService")
class EmailService {

@Inject
private UserService userService;
@Command("sendEmail")
public String sendEmail(String to, String subject) {
return "Email sent to: " + to + " - " + subject;
}
@Scheduled(cron = "0 0 9 * * *") // Daily at 9 AM
public void sendDailyReports() {
System.out.println("Sending daily reports...");
}

}

// Simple DI Container and Framework
class ApplicationContext {
private Map services = new HashMap<>();
private Map commands = new HashMap<>();

public ApplicationContext(Class<?>... configClasses) {
// Scan and instantiate services
for (Class<?> configClass : configClasses) {
scanPackage(configClass.getPackage());
}
// Inject dependencies
injectDependencies();
// Register commands
registerCommands();
}
private void scanPackage(Package pkg) {
// In real implementation, you would scan the package
// For this example, we'll manually register our services
registerService(new UserService());
registerService(new EmailService());
}
private void registerService(Object service) {
Class<?> serviceClass = service.getClass();
Service serviceAnnotation = serviceClass.getAnnotation(Service.class);
String serviceName = serviceAnnotation != null && !serviceAnnotation.name().isEmpty() 
? serviceAnnotation.name() 
: serviceClass.getSimpleName();
services.put(serviceName, service);
System.out.println("Registered service: " + serviceName);
}
private void injectDependencies() {
for (Object service : services.values()) {
for (Field field : service.getClass().getDeclaredFields()) {
if (field.isAnnotationPresent(Inject.class)) {
Inject inject = field.getAnnotation(Inject.class);
String dependencyName = inject.value().isEmpty() 
? field.getType().getSimpleName() 
: inject.value();
Object dependency = services.get(dependencyName);
if (dependency != null) {
field.setAccessible(true);
try {
field.set(service, dependency);
System.out.println("Injected " + dependencyName + " into " + 
service.getClass().getSimpleName());
} catch (IllegalAccessException e) {
System.err.println("Failed to inject " + dependencyName + ": " + e.getMessage());
}
}
}
}
}
}
private void registerCommands() {
for (Object service : services.values()) {
for (Method method : service.getClass().getDeclaredMethods()) {
Command command = method.getAnnotation(Command.class);
if (command != null) {
commands.put(command.value(), method);
System.out.println("Registered command: " + command.value() + 
" - " + command.description());
}
}
}
}
public Object executeCommand(String commandName, Object... args) {
Method method = commands.get(commandName);
if (method == null) {
throw new IllegalArgumentException("Unknown command: " + commandName);
}
try {
Object service = services.values().stream()
.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper