← Назад к вопросам

Что такое аннотации в Java? Как создать свою аннотацию?

2.0 Middle🔥 171 комментариев
#Docker, Kubernetes и DevOps#JVM и управление памятью

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Аннотации в Java

Аннотации — это форма метаданных, которые предоставляют информацию о коде, но не являются частью самого кода. Они не прямо влияют на выполнение кода, но могут использоваться инструментами, фреймворками и компилятором для обработки информации.

Встроенные аннотации

Java содержит несколько встроенных аннотаций:

@Override        // указывает, что метод переопределяет метод из базового класса
@Deprecated      // указывает, что код устарел и больше не рекомендуется
@SuppressWarnings // указывает компилятору игнорировать определённые предупреждения
@FunctionalInterface // указывает, что интерфейс функциональный

Примеры использования:

class Animal {
    public void makeSound() {
        System.out.println("Some sound");
    }
}

class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Woof");
    }
    
    @Deprecated
    public void oldMethod() {
        System.out.println("This is deprecated");
    }
}

public class Main {
    public static void main(String[] args) {
        @SuppressWarnings("unchecked")
        List rawList = new ArrayList();  // компилятор не выдаст warning
    }
}

Метааннотации

Метааннотации — это аннотации для аннотаций. Они определяют, как работают другие аннотации:

@Retention     // указывает, как долго аннотация сохраняется
@Target        // указывает, к чему можно применить аннотацию
@Documented    // указывает, что аннотация будет включена в Javadoc
@Inherited     // указывает, что аннотация наследуется подклассами
Repeatable    // указывает, что аннотация может быть применена несколько раз

Значения @Retention:

  • RetentionPolicy.SOURCE — удаляется после компиляции
  • RetentionPolicy.CLASS — сохраняется в .class файле, но не доступна в runtime
  • RetentionPolicy.RUNTIME — доступна во время выполнения (через reflection)

Значения @Target:

  • ElementType.TYPE — для классов, интерфейсов, enum
  • ElementType.FIELD — для полей
  • ElementType.METHOD — для методов
  • ElementType.PARAMETER — для параметров методов
  • ElementType.CONSTRUCTOR — для конструкторов
  • ElementType.LOCAL_VARIABLE — для локальных переменных

Создание собственной аннотации

Пример 1: Простая аннотация для валидации

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface NotEmpty {
    String message() default "Поле не должно быть пустым";
}

Пример 2: Более сложная аннотация

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
public @interface ApiEndpoint {
    String path();
    String[] methods() default {"GET"};
    String description() default "";
    boolean requiresAuth() default true;
}

Использование собственных аннотаций

@ApiEndpoint(
    path = "/users",
    methods = {"GET", "POST"},
    description = "Получение списка пользователей",
    requiresAuth = true
)
public class UserController {
    
    @ApiEndpoint(
        path = "/users/{id}",
        methods = {"GET"},
        description = "Получение пользователя по ID"
    )
    public User getUser(String id) {
        return new User(id);
    }
}

Обработка аннотаций в runtime

import java.lang.reflect.*;

public class AnnotationProcessor {
    public static void processAnnotations(Class<?> clazz) {
        // Проверяем аннотации класса
        if (clazz.isAnnotationPresent(ApiEndpoint.class)) {
            ApiEndpoint endpoint = clazz.getAnnotation(ApiEndpoint.class);
            System.out.println("Path: " + endpoint.path());
            System.out.println("Methods: " + Arrays.toString(endpoint.methods()));
            System.out.println("Description: " + endpoint.description());
        }
        
        // Проверяем аннотации методов
        for (Method method : clazz.getDeclaredMethods()) {
            if (method.isAnnotationPresent(ApiEndpoint.class)) {
                ApiEndpoint endpoint = method.getAnnotation(ApiEndpoint.class);
                System.out.println("Method: " + method.getName());
                System.out.println("Path: " + endpoint.path());
            }
        }
        
        // Проверяем аннотации полей
        for (Field field : clazz.getDeclaredFields()) {
            if (field.isAnnotationPresent(NotEmpty.class)) {
                NotEmpty notEmpty = field.getAnnotation(NotEmpty.class);
                System.out.println("Field: " + field.getName());
                System.out.println("Message: " + notEmpty.message());
            }
        }
    }
}

Валидация с собственной аннотацией

public class User {
    @NotEmpty(message = "Имя пользователя не может быть пустым")
    private String username;
    
    @NotEmpty
    private String email;
    
    public User(String username, String email) {
        this.username = username;
        this.email = email;
    }
}

public class Validator {
    public static boolean validate(Object obj) {
        Class<?> clazz = obj.getClass();
        
        for (Field field : clazz.getDeclaredFields()) {
            if (field.isAnnotationPresent(NotEmpty.class)) {
                field.setAccessible(true);
                try {
                    Object value = field.get(obj);
                    if (value == null || (value instanceof String && ((String) value).isEmpty())) {
                        NotEmpty annotation = field.getAnnotation(NotEmpty.class);
                        System.out.println(annotation.message());
                        return false;
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return true;
    }
}

Практическое применение

public class Main {
    public static void main(String[] args) {
        // Валидация
        User user1 = new User("", "test@example.com");
        System.out.println("User1 valid: " + Validator.validate(user1)); // false
        
        User user2 = new User("john", "john@example.com");
        System.out.println("User2 valid: " + Validator.validate(user2)); // true
        
        // Обработка аннотаций
        AnnotationProcessor.processAnnotations(UserController.class);
    }
}

Где используются аннотации?

  • JUnit — @Test, @Before, @After
  • Spring — @Autowired, @Service, @Repository, @RequestMapping
  • Hibernate — @Entity, @Column, @Id
  • JAX-RS — @Path, @GET, @POST
  • Project Lombok — @Data, @Getter, @Setter

Примечания

  • Аннотации НЕ влияют на выполнение кода напрямую
  • Они используются для метапрограммирования и инструментов обработки
  • Runtime аннотации требуют обработки через reflection
  • При создании аннотаций всегда используй @Retention(RetentionPolicy.RUNTIME) если нужна обработка в runtime

Аннотации — мощный инструмент для создания фреймворков, библиотек и для делегирования повторяющихся задач инструментам вместо написания кода вручную.