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

Сколько видов @Retention знаешь?

1.3 Junior🔥 131 комментариев
#Основы Java

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

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

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

@Retention: три уровня хранения аннотаций

Основная концепция

@Retention контролирует, на каком этапе жизненного цикла программы аннотация остаётся доступной. Есть три значения (RetentionPolicy):

1. RetentionPolicy.SOURCE

Аннотация существует только в исходном коде, удаляется компилятором.

@Retention(RetentionPolicy.SOURCE)
public @interface Deprecated {
    // Используется только в .java файлах
    // При компиляции удаляется
    // В .class файле НЕ будет
    // В runtime НЕ доступна
}

// Пример использования
@SuppressWarnings("unchecked")  // SOURCE - удалится при компиляции
public class OldClass {
    // Компилятор видит аннотацию
    // но в .class файле её не будет
}

Когда использовать:

  • Аннотации для компилятора (@Override, @SuppressWarnings)
  • Аннотации для инструментов анализа кода
  • Подсказки для IDE
  • Не нужны в runtime

Примеры:

// @Override - помощь компилятору при переопределении
@Override
public void run() {
    // Если метод не переопределяет родительский - ошибка компиляции
}

// @SuppressWarnings - отключение предупреждений компилятора
@SuppressWarnings("unchecked")
public List getList() {
    return new ArrayList();  // без аннотации - warning
}

// Своя аннотация для компилятора
@Retention(RetentionPolicy.SOURCE)
public @interface NotNull {
    // IDE подсветит если передать null
}

Преимущества:

  • Минимальный размер .class файла
  • Нет overhead на runtime
  • Компилятор может проверить корректность

Недостатки:

  • Недоступна в runtime
  • Нельзя использовать для reflection

2. RetentionPolicy.CLASS

Аннотация хранится в .class файле, но недоступна в runtime через reflection.

@Retention(RetentionPolicy.CLASS)
public @interface MyAnnotation {
    // Будет в .class файле
    // НЕ будет в runtime при работе программы
    // Нельзя получить через reflection
    // Доступна инструментам обработки bytecode
}

// Пример
@MyAnnotation(value = "test")
public class MyClass {
    // Компилятор добавит информацию в .class
    // Но если запросить при runtime - null
}

MyAnnotation annotation = MyClass.class.getAnnotation(MyAnnotation.class);
// annotation будет null!

Когда использовать:

  • Аннотации для инструментов bytecode instrumentation
  • Аннотации для APM (Application Performance Monitoring)
  • Аннотации для code coverage (Jacoco)
  • Не нужны при выполнении программы

Примеры:

// Для инструмента code coverage
@Retention(RetentionPolicy.CLASS)
public @interface InstrumentForCoverage {
    // Jacoco добавит информацию для подсчёта покрытия
}

// Для APM инструмента
@Retention(RetentionPolicy.CLASS)
public @interface Trace {
    // Инструмент обработает bytecode и добавит логирование
}

// Для статического анализатора
@Retention(RetentionPolicy.CLASS)
public @interface AnalyzeMe {
    // FindBugs / SpotBugs будут анализировать
}

Это значение по умолчанию если @Retention не указана:

public @interface DefaultRetention {
    // Если нет @Retention - используется CLASS
    // Будет в .class но не в runtime
}

Преимущества:

  • Доступна инструментам
  • Не требует runtime processing
  • Минимальный overhead

Недостатки:

  • Недоступна для reflection в runtime

3. RetentionPolicy.RUNTIME

Аннотация сохраняется в runtime и доступна через reflection.

@Retention(RetentionPolicy.RUNTIME)
public @interface MyRuntimeAnnotation {
    String value() default "";
    int priority() default 0;
    // Будет в .class файле
    // Будет доступна в runtime через reflection
}

// Использование
@MyRuntimeAnnotation(value = "important", priority = 1)
public class MyClass {
    // Аннотация будет доступна при выполнении
}

// Получение в runtime
MyRuntimeAnnotation annotation = MyClass.class
    .getAnnotation(MyRuntimeAnnotation.class);

if (annotation != null) {
    System.out.println(annotation.value());     // "important"
    System.out.println(annotation.priority());  // 1
}

Когда использовать:

  • Dependency Injection (Spring @Autowired, @Component)
  • Сериализация (JSON @JsonProperty)
  • ORM (JPA @Entity, @Column)
  • Test frameworks (JUnit @Test, @Before)
  • Кастомные фреймворки требующие reflection

Примеры из популярных фреймворков:

// Spring Framework
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Autowired {
    // Spring при startup ищет через reflection
    // и внедряет зависимости
}

// JPA
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Entity {
    // Hibernate ищет через reflection
    // и создаёт映射
}

// JUnit
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test {
    // Фреймворк ищет методы с этой аннотацией
    // и запускает их как тесты
}

// Свой фреймворк
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiredRole {
    String value();
}

public class SecurityFilter {
    public void filter(Method method) {
        RequiredRole annotation = method
            .getAnnotation(RequiredRole.class);
        
        if (annotation != null) {
            String requiredRole = annotation.value();
            // Проверить, есть ли у пользователя роль
            if (!userHasRole(requiredRole)) {
                throw new AccessDeniedException();
            }
        }
    }
}

Практический пример: простой Dependency Injection контейнер

@Retention(RetentionPolicy.RUNTIME)
public @interface Inject {
}

public class DIContainer {
    public <T> T getInstance(Class<T> clazz) {
        T instance = clazz.newInstance();
        
        // Ищем поля с @Inject
        for (Field field : clazz.getDeclaredFields()) {
            if (field.isAnnotationPresent(Inject.class)) {
                // Инъектируем зависимость
                Object dependency = getInstance(field.getType());
                field.setAccessible(true);
                field.set(instance, dependency);
            }
        }
        
        return instance;
    }
}

// Использование
public class UserService {
    @Inject
    private UserRepository userRepository;  // автоматически внедрится
}

UserService service = new DIContainer().getInstance(UserService.class);

Преимущества:

  • Доступна при выполнении программы
  • Можно использовать reflection для обработки
  • Гибко: фреймворк может принять решение в runtime

Недостатки:

  • Больший размер .class файла
  • Overhead на runtime (reflection)
  • Нужно обрабатывать вручную

Сравнение трёх видов

PolicyВ исходном кодеВ .class файлеВ runtimeReflectionКогда использовать
SOURCEПодсказки компилятору, IDE
CLASSИнструменты bytecode, coverage
RUNTIMEФреймворки, DI, ORM

Как проверить уровень retention

public class AnnotationUtils {
    public static void checkRetention(Class<?> annotationClass) {
        Retention retention = annotationClass
            .getAnnotation(Retention.class);
        
        if (retention == null) {
            System.out.println("Default: CLASS");
        } else {
            RetentionPolicy policy = retention.value();
            System.out.println("Policy: " + policy);
        }
    }
}

AnnotationUtils.checkRetention(Override.class);      // SOURCE
AnnotationUtils.checkRetention(Entity.class);        // RUNTIME

Практическое правило

// Использовать SOURCE если:
// - Нужна только для компилятора
// - Не нужна в runtime
@Override  // Ошибка компиляции если неправильно
public void method() {}

// Использовать CLASS если:
// - Нужна инструментам bytecode
// - НЕ нужна в runtime
// Это значение по умолчанию
@interface BytecodeInstruction {}

// Использовать RUNTIME если:
// - Нужна в runtime
// - Нужна reflection
// - Используется в фреймворке
@Retention(RetentionPolicy.RUNTIME)
@interface Framework {}

Вывод

Три вида @Retention:

  1. SOURCE - только в исходном коде, удаляется компилятором
  2. CLASS - в .class файле, но не в runtime (по умолчанию)
  3. RUNTIME - сохраняется и доступна через reflection при выполнении

Выбирай в зависимости от того, когда нужна информация об аннотации: когда компилируется, когда обрабатывается bytecode, или когда запущена программа.