Введение в аннотации

Последнее обновление: 01.11.2025

Аннотации в языке программирования Java представляют специальную форму метаданных (данных о данных), которую можно добавлять в исходный код. Они предоставляют дополнительную информацию о программе, которая может использоваться компилятором, инструментами сборки, фреймворками или во время выполнения. Аннотации позволяет отделить конфигурацию и метаданные от основной бизнес-логики. Они делают код более чистым, декларативным и легким для поддержки, перенося большую часть "магии" настройки на фреймворки и инструменты. Можно сказать, что аннотации являются основой многих современных Java-технологий, в частности, веб-фреймворков, фреймворков для тестирования, валидации и т.д.

Сами по себе аннотации не выполняют никаких действий. Они лишь "маркируют" классы, методы, поля и другие элементы кода, а уже другой код (обработчик аннотаций) ищет эти маркеры и выполняет на их основе некоторую определенную логику.

Синтаксически аннотация начинается с символа @, за которым следует ее имя, например, @Override.

В языке Java есть ряд встроенных аннотаций, которые используются для стандартных задач. Рассмотрим некоторые наиболее используемые:

  • @Override: указывает компилятору, что текущий метод переопределяет метод из суперкласса.

  • @Deprecated: эта аннотация помечает элемент (класс, метод, поле) как устаревший

  • @SuppressWarnings: отключает определенные предупреждения компилятора для аннотированного элемента.

  • @FunctionalInterface: указывает, что интерфейс предназначен для использования в качестве функционального интерфейса (т.е. он должен иметь ровно один абстрактный метод).

Применение аннотаций

Аннотации объявлений могут применяться ко многим компонентам. И в зависимости от места применения аннотации делятся на 2 группы:

  • аннотации объявлений (declaration annotation)

  • аннотации использования типов (type uses annotation)

Аннотации объявлений

Аннотации объявлений содержат некоторую информацию об объявляемом элементе и могут применяться к следующим объявлениям:

  • К классам (включая перечисления) и интерфейсам (в том числе к интерфейсам аннотаций)

  • К методам

  • К конструкторам

  • К полям (в том числе к константам перечислений и полям классов record).

  • К локальным переменным (в том числе к объявленным в конструкциях for и try-with-resources).

  • К параметрам методов

  • К параметрам типов

  • К пакетам и модулям

Для классов, интерфейсов и модулей аннотации размещаются перед ключевым словом class, interface или module и любым модификаторам (при его наличии):

@MyAnnotation public class Person { }

Для переменных, параметров, а также поле классов-record аннотации размещаются перед типом:

@MyAnnotation String name = "Tom";
public User getUser(@MyAnnotation int userId)
record Person(@MyAnnotation String name) {}

Также аннотация помещается перед параметром типа в обобщенном классе или методе:

class Person<@MyAnnotation T> { }

Аннотации использования типов

Аннотации использования типов могут появляться в следующих местах:

  • С аргументами обобщенного типа:

    Person<@MyAnnotation String>
  • Перед скобками массива:

    String[] @MyAnnotation [] data // Применяется к String[]
    String @MyAnnotation [][] data // Применяется к String[][]
    

    От эих ситуаций надо отличать следующую:

    @MyAnnotation String[][] data

    Здесь аннотация применяется к String

  • С суперклассами и реализованными интерфейсами:

    class Employee extends @MyAnnotation Person.
  • С вызовами конструктора

     new @MyAnnotation Person();
  • С вложенными типами

     Map.@MyAnnotation Entry
  • С приведениями типов и проверками instanceof:

    (@MyAnnotation String) text
    if (text instanceof @MyAnnotation String)
    

    Аннотации предназначены только для использования внешними инструментами. Они не влияют на поведение приведения типов или проверки instanceof.

  • С указанием генерируемых исключений

    void print() throws @MyAnnotation Exception.
  • С подстановочными типами и ограничениями типов

    List<@MyAnnotation ? extends Person>
    List<? extends @MyAnnotation Person>
    
  • С ссылками на методы и конструктор:

    @MyAnnotation Person::getName

Аннотации можно помещать до или после других модификаторов, таких как static и public. Обычно аннотации использования типа размещаются после других модификаторов, а аннотации объявлений - перед другими модификаторами. Например:

private @MyAnnotation String name;   // Аннотация использования типа
@MyAnnotation private int age;      // Аннотация переменной / поля класса

Встроенные (стандартные) аннотации Java

@Override

Аннотация @Override указывает компилятору, что текущий метод переопределяет метод из суперкласса. Если метод с такой сигнатурой в суперклассе не найден, компилятор выдаст ошибку. Это помогает избежать опечаток.

Конечно, при переопределении метода суперкласса в производном классе нам необязательно использовать данную аннотацию. Но рассмотрим следующую программу:

class Animal {
    void makeSound() {
        System.out.println("Некий звук");
    }
}
class Cat extends Animal {
    
    void makeSounds() {
        System.out.println("Мяу");
    }
}

class Program{
 
    public static void main(String[] args) throws Exception{
          
        Cat barsik = new Cat();
        barsik.makeSound(); // Некий звук
    }
}

Класс Animal представляет животное и определяет метод makeSound() для имитации звука животного. Класс Cat переопределяет этот метод. А в методе main() вызываем этот метод. Но посмотрим на консольный вывод программы:

Некий звук

Результат, очевидно, не тот, который ожидался. А почему? А потому что я допустил опечатку: вместо "makeSound" в классе Cat определил метод "makeSounds". В итоге я по сути определил новый метод, который ничего не переопределяет. Но программа была успешно скомпилирована и успешно выполнила все свои действия! И в том числе для предупреждения подобных ситуаций предназначена аннотация @Override. Применим ее:

class Animal {
    void makeSound() {
        System.out.println("Некий звук");
    }
}
class Cat extends Animal {
    
    @Override
    void makeSounds() {
        System.out.println("Мяу");
    }
}

В производном классе Cat попрежнему метод с опечаткой: "makeSounds" вместо "makeSound". Однако? применив к методу аннотацию @Override, мы указываем, что в суперклассе должен быть метод с подобным именем, и мы его хотим переопределить. Но поскольку в суперклассе нет метода "makeSounds", то при компиляции теперь мы получим ошибку:

Program.java:8: error: method does not override or implement a method from a supertype
    @Override
    ^
1 error
error: compilation failed

Таким образом, мы увидим ошибку и сможем своевременно ее исправить, недопустив некорректного выполнения программы:

class Cat extends Animal {
    
    @Override
    void makeSound() {
        System.out.println("Мяу");
    }
}

@Deprecated

Аннотация @Deprecated помечает элемент (класс, метод, поле) как устаревший, что может быть полезно, если после обновления API класса или пакета, мы хотим, что какие-то компоненты больше не использовались. Компилятор будет выдавать предупреждение при каждой попытке использовать такой компонент. Например:

class Cat{
    
    @Deprecated
    void makeSound() {
        System.out.println("Мяу");
    }

    void say() {
        System.out.println("Мяу");
    }
}

class Program{
 
    public static void main(String[] args) throws Exception{
          
        Cat barsik = new Cat();
        barsik.makeSound();
    }
}

Здесь в классе Cat определен метод makeSound(), который с помощью аннотации @Deprecated объявлен как устаревший (допустим, мы хотим, чтобы в будущем разработчики использовали метод say(), который дублирует функциональность метода makeSound()). Данная программу успешно скомпилируется и будет выполняться, но при компиляции компилятор выдаст предупреждение:

eugene@Eugene:/workspace/java$ java Program.java
Program.java:18: warning: [deprecation] makeSound() in Cat has been deprecated
        barsik.makeSound();
              ^
1 warning
Мяу

@SuppressWarnings

Аннотация @SuppressWarnings отключает определенные предупреждения компилятора для аннотированного элемента. Например:

import java.util.ArrayList;
import java.util.List;

class Program{
 
    public static void main(String[] args) throws Exception{
          
        methodWithRawType();
    }

    public static void methodWithRawType() {
        List names = new ArrayList();
        names.add("Tom");
        System.out.println(names);
    }
}

Эта программа успешно скомпилируется и выполняется, однако при компиляции мы получим предупреждение:

eugene@Eugene:/workspace/java$ java Program.java
Program.java:13: warning: [unchecked] unchecked call to add(E) as a member of the raw type List
        names.add("Tom");
                 ^
  where E is a type-variable:
    E extends Object declared in interface List
1 warning
[Tom]

Проблема в том, что нам надо указать тип хранимых данных для ArrayList, наподобие следующего:

List<String> names = new ArrayList<String>();

Однако мы также можем применить аннотацию @SuppressWarnings для отключения подобного (и других) предупреждений. Для этого после названия аннотации в скобках надо передать строку с типом предупреждения. В примере выше мы тип предупреждения - "unchecked", соответственно нам надо написать:

@SuppressWarnings("unchecked")
public static void methodWithRawType() {

    List names = new ArrayList();
    names.add("Tom");
    System.out.println(names);
}

@FunctionalInterface

Аннотация @FunctionalInterface указывает, что интерфейс предназначен для использования в качестве функционального интерфейса (т.е. он должен иметь ровно один абстрактный метод). Компилятор проверяет это условие.

@FunctionalInterface
interface MyComparator {
    int compare(String a, String b);
}
Помощь сайту
Юмани:
410011174743222
Номер карты:
4048415020898850