Аннотации в Java
Last updated: 4 мая 2025 г.Поясним что такое аннотации в Java с помощью небольшой анналогии.
Представьте себе склад с коробками. На каждой из коробок есть надпись, которая говорит тому, кто будет отгружать эти коробки из склада, куда эту коробку отгружать.
По этой аналогии коробка, — это класс, переменная или метод, аннотация, — это надпись, а тот кто отгружает коробку и читает надпись, — это компилятор Java. Компилятор смотрит на аннотацию над классом, методом или переменной и выполняет соответствующие действия.
Например с помощью аннотации SafeVarargs
над классом можно подавить некоторые предупреждения, которые компилятор бы выдал если бы над этим классом не было этой аннотации. То есть компилятор увидел над классом аннотацию SafeVarargs
, которая говорит компилятору “не выдавай предупреждения о классе” и он этого делать не будет.
Или аннотация Documented
скажет компилятору задокументировать класс над которым она стоит.
Далее на примерах разберем некоторые важные аннотации.
Аннотация Override
Override
— наверное, самая часто встречающаяся аннотация. Этой аннотацией помечается метод чтобы всем программистам смотрящим на него было видно, что метод переопределяется. То есть в родительском классе того класса, в котором находится метод с аннотацией @Override
, уже определён метод с таким же именем и сигнатурой.
Поясним на примере:
1import java.util.*;
2import java.lang.annotation.*;
3// Определим родительский класс
4class ParentClass {
5 // Этот метод будет переопределяться наследником.
6 public String someMethod() { return "parent method"; }
7}
8
9public class Annotations extends ParentClass {
10 // Аннотация Override сообщает программисту,
11 // который смотрит на метод ниже, что происходит
12 // переопределение метода родительского класса.
13 @Override
14 public String someMethod() {
15 return "overrided!";
16 }
17
18 public static void main(String[] args) {
19 Annotations test = new Annotations();
20 System.out.println(test.someMethod());
21 }
22}
Вывод:

Также еще служит как страховка.
То есть представим, что мы например случайно удалили или закомментировали метод в родительском классе, который переопределялся наследником. Если этот переопределяемый в наследнике метод был помечен аннотацией Override
, то при запуске компилятор выдаст соответствующую ошибку сообщающую о том, что аннотация Override
здесь ни к чему, поскольку в родительском методе нет метода с таким названием. В примере ниже в ParentClass
мы не определили метод с именем someMethod
(точнее метод закомментирован, как видим), поэтому программа выдаст ошибку.
1import java.util.*;
2import java.lang.annotation.*;
3
4// Определим родительский класс
5class ParentClass {
6 /*
7 public String someMethod() {return "parent method";}
8 */
9}
10
11public class Override1 extends ParentClass {
12
13 @Override // аннотация
14 public String someMethod() {return "overridden";}
15
16 public static void main(String[] args) {
17 Override1 test = new Override1();
18 test.someMethod();
19 }
20}
Вывод:

Аннотация @FunctionslInterface
FunctionalInterface
– если над интерфейсом написать аннотацию @FunctionalInterface
, то в такой интерфейс нельзя будет добавить более одного абстрактного метода (интерфейс с одним единственным абстрактным методом называется функциональным интерфейсом).
То есть если код содержит в себе интерфейс, который помечен аннотацией FunctionalInterface и который при этом содержит несколько абстрактных методов, то такой код не скомпилируется.
Что такое функциональный интерфейс, зачем он нужен и как им пользоваться разберем немного позже.
Поясним на примере:
1// Deprecated и FunctionalInterface
2import java.util.*;
3import java.lang.annotation.*;
4
5@FunctionalInterface
6interface Print {
7 // Здесь в функциональном интерфейсе должен быть
8 // один абстрактный метод, а их как видим два.
9 void printString(String testString);
10 void printAnotherString(String testString);
11}
12
13public class AnotherAnnotations {
14 public static void main(String[] args) {
15 AnotherAnnotations swDemo = new AnotherAnnotations();
16 }
17}
Вывод:

Как видим код не скомпилировался.
Аннотация @Deprecated
Deprecated
– для пометки устаревших методов или классов.
При вызове метода вызовется предупреждение, что метод устарел и что его лучше не использовать.
Поясним на примере:
1import java.util.*;
2import java.lang.annotation.*;
3
4class ParentClass {
5 public String getName() { return "mike"; }
6
7 // метод ниже устарел
8 @Deprecated
9 public void deprecatedMethod() {
10 System.out.println("This is a legacy function");
11 }
12}
13
14public class AnotherAnnotations extends ParentClass {
15 public static void main(String[] args) {
16 AnotherAnnotations swDemo = new AnotherAnnotations();
17 swDemo.deprecatedMethod();
18 }
19}
Вывод:

Как видим, код скомпилировался, но при компиляции было вызвано предупреждение.
Создание собственных (кастомных) аннотаций.
Можно создавать свои аннотации с помощью аннотации @interface
.
При создании аннотации используются вспомогательные аннотации. Они пишутся над аннотацией @interface.
@Target
– этой аннотацией указываем к чему будет применяться создаваемая аннотация – если укажем TYPE, то только к классу, если METHOD, то только к методу, если FIELD, то только к полю, есть и другие значения, но это основные
@Retention
– этой аннотацией указываем где будет жить создаваемая аннотация – если укажем RUNTIME, то создаваемая аннотация должна быть доступна джава машине во время выполнения, если CLASS, то аннотация не будет доступна во время выполнения, но будет в скомпилированном джава class файле, если SOURCE, то создаваемая аннотация будет содержаться только в исходном коде.
@Repeatable(...)
– создаваемая аннотация может использоваться несколько раз на методе, классе или поле. В скобочках указывается другая кастомная аннотация. Эта другая кастомная аннотация будет хранить в виде массива ВСЕ повторения применения над конкретным методом или классом, или полем той кастомной аннотации над которой мы писали Repeatable.
@Inherited
– создаваемая аннотация наследуется классами, которые наследуют класс к которому применена аннотация.
@Documented
– информация о создаваемой аннотации появиться в JavaDoc-документации
Поясним на примере:
1import java.util.*;
2import java.lang.annotation.*;
3
4//Создадим аннотацию с именем RepeatableCompany
5//Для начала к создаваемой аннотации RepeatableCompany
6//применим необходимые вспомогательные аннотации.
7
8//Применим @Inherited. Значит, что к наследникам
9//классов к которым будет применена создаваемая
10//аннотация тоже будет применена эта аннотация.
11//То есть можно увидеть, что аннотация
12//RepeatableCompany применена к классу CompanyClass,
13//а значит и к классу CreateAnnotation она тоже будет
14//применена так как CreateAnnotation это наследник
15//класса CompanyClass.
16@Inherited
17
18//Применим @Documented.
19//Значит создаваемая аннотация будет задокументирована
20@Documented
21
22//Применим @Target(ElementType.TYPE).
23//Значит создаваемая аннотация будет использоваться
24//над классами
25@Target(ElementType.TYPE)
26
27//Применим @Repeatable(RepeatableCompanies.class).
28//Значит создаваемую аннотацию можно будет применять
29//несколько раз на один и тот же класс.
30//RepeatableCompanies – это аннотация, которая
31//содержит массив из всех аннотаций RepeatableCompany,
32//примененных к классу и к какому нибудь
33//классу храниться в RepeatableCompanies.
34@Repeatable(RepeatableCompanies.class)
35
36//Применим @Retention(RetentionPolicy.RUNTIME). Значит
37//создаваемая аннотация доступна при работе программы.
38@Retention(RetentionPolicy.RUNTIME)
39
40//Теперь приступим непосредственно
41//к созданию @RepeatableCompany.
42@interface RepeatableCompany {
43 //При создании аннотации в ней можно определять
44 //поля аннотации. Ниже можно увидеть пример
45 //пары полей. В аннотации они определяются чтобы
46 //при применении аннотации над каким-то классом,
47 //можно было записываться в такие
48 //поля какие-то данные которые будут
49 //доступны при выполнении над объектом класса
50 //к которому была применена аннотация.
51 //Ниже можно увидеть пример аннотации которая
52 //будет создана просто и не будет выполнять
53 //никаких действий над классом к которому она
54 //будет применяться, поля аннотации будут
55 //содержать какие-то данные которые можно будет
56 //использовать при выполнении программы.
57 //Пример ниже показывает как аннотация смотрит
58 //внутри и как она будет применяться.
59 //Например:
60 @RepeatableCompany(name="Toyota Motor",city="Tokyo")
61 //аннотация будет видна в коде класса
62 //CompanyClass в котором она указана.
63 String name() default "CompanyName";
64 String city() default "CompanyCity";
65 //default это дефолтное значение поля.
66 //То есть программист может не указывать
67 //name = "Toyota Motor", city = "Tokyo" в скобочках
68 //у аннотации, а просто применить над классом
69 //аннотацию без ничего вот так: @RepeatableCompany.
70 //И так как в скобочках ничего не было указано
71 //то name и city будут содержать дефолтные
72 //значения – "CompanyName" и "CompanyCity".
73}
74
75@Documented
76@Inherited
77@Target(ElementType.TYPE)
78@Retention(RetentionPolicy.RUNTIME)
79@interface RepeatableCompanies {
80 //массив аннотаций RepeatableCompany
81 RepeatableCompany[] value() default{};
82}
83
84@RepeatableCompany
85@RepeatableCompany(name = "Toyota Motor",city = "Tokyo")
86@RepeatableCompany(name = "Woltzvagen",city = "Germany")
87class CompanyClass {
88 public String getName() {return "mike";}
89}
90
91class CreateAnnotation extends CompanyClass {
92 public static void main(String[] args) {
93 //Созданная аннотация чисто для информации
94 //программисту, поэтому в мейн ничего не делаем.
95 }
96}
Дополнительные материалы
Официальный гайд по аннотациям от Oracle
Статья о создании кастомных аннотаций
Создание кастомных аннотаций (JournalDev)
Следующие уроки
Обработка исключений
10
мин.
Generics в Java
9
мин.
Приведение типов
8
мин.