Как написать аннотацию на Java за 5 шагов
Разбираемся с аннотациями в Java: пошаговое руководство.
AD Design Award 2021
Аннотации нужны для описания метаданных — дополнительной информации о программных элементах. Они могут использоваться, например, для конфигурирования программного окружения или для выдачи указаний компилятору: чтобы он учитывал эти аннотации при проверке ошибок или подавлении предупреждений.
Аннотации — удобный способ собрать данные о сущности и правила их обработки в одном месте.
В Java есть встроенные аннотации. Например:
- @Override у метода — говорит о том, что родительский метод переопределён в наследнике. Компилятор при наличии такой аннотации проверяет, не нарушены ли правила переопределения.
- @Deprecated помечаются элементы языка, которые больше не рекомендуется использовать, так как они заменены другими.
Но нам мало встроенных аннотаций. Поэтому научимся создавать свои: начнём с простой маркерной и за пять шагов дойдём до вершины мастерства — повторяющейся аннотации.
Текущий релиз Java SE с долгосрочной поддержкой (LTS) — Java 11. Поэтому описание синтаксиса и примеры ниже даны для этой версии языка.
Шаг нулевой, подготовительный
Формулируем задачу и описываем контекст
Потренируемся в написании аннотаций на примере Telegram-каналов: недавно мне попался очередной опрос от создателей мессенджера, в котором был и вопрос о числе подписок на каналы. Внезапно оказалось, что у меня их больше 30, самых разных, — вот пусть и поработают подопытными кроликами.
Создадим аннотации, показывающие, насколько интересен контент канала и какого рода информация там публикуется, ну а дальше как пойдёт 😀
Шаг первый
Создаём каркас аннотации
Аннотации похожи на интерфейсы. Если вы пока не очень ориентируетесь в интерфейсах, освежите свои знания о них в этой статье, перед тем как мы продолжим.
Они не просто похожи на интерфейсы: под капотом JVM (виртуальная машина Java) преобразует элементы аннотации в методы интерфейса, а саму аннотацию — в имплементацию (реализацию) этого интерфейса.
Вот как выглядит объявление простейшей аннотации для отметки особо интересных каналов:
Здесь, кроме заголовка, нет ничего полезного. Такие аннотации ещё называют маркерными — они действительно просто маркируют, обозначают какой-то признак.
Как использовать:
Или так:
При использовании маркерных аннотаций можно опускать круглые скобки после названия.
В аннотациях могут быть:
- обязательные элементы,
- необязательные элементы,
- константы.
Шаг второй
Добавляем обязательные элементы
Напишем аннотацию, которой будем обозначать каналы о хобби. У неё будет один обязательный элемент — собственно описание хобби: фотография, музыка, путешествия и тому подобное.
После названия элемента обязательны круглые скобки — как для методов в интерфейсе. Набор типов для элементов ограничен, можно использовать только:
- примитивные типы (byte, short, int, long, float, double, boolean, char);
- String;
- Class;
- перечисление (enum);
- другую аннотацию;
- массив любого из вышеперечисленных типов.
Вот так можно:
А так нельзя:
Для элементов аннотаций можно использовать модификаторы, но не любые, потому что, как и методы интерфейсов, все элементы аннотаций неявно public и abstract.
Это значит, что можно определить элемент таким образом:
Но нельзя, например, так:
final здесь недопустим, потому что конфликтует с неявным abstract. Это выглядит логичным, если вспомнить, что final запрещает переопределять метод, а abstract, напротив, требует реализации в наследниках. Исключение — только если наследник тоже абстрактный.
Как использовать:
Каждому обязательному элементу необходимо задать значение — на то они и обязательные. Для определения элемента-массива указываются фигурные {} скобки. Если в массиве только один элемент (как во втором примере с музыкальным каналом), скобки можно опустить.
Где-то в коде вам может попасться аннотация, у которой в скобках просто написано значение элемента без названия, вот так:
Можете быть уверены, что у этой аннотации есть элемент с особым названием:
- он называется value;
- он обязательный;
- других обязательных элементов нет.
Необязательные элементы допустимы, но если захотите изменить их значения по умолчанию, придётся явно указать и название элемента value:
Шаг третий
Добавляем необязательные элементы
Сделаем необязательными все элементы, кроме kind. Для этого всем остальным элементам нужно задать значения по умолчанию.
Вот как это делается:
В качестве дефолтного значения может использоваться только константное НЕ null-значение. То есть вот такие объявления компилятор не пропустит:
Зато пустую строку в качестве значения по умолчанию использовать можно, вот так:
Как использовать:
Текста по сравнению с предыдущим пунктом стало меньше, потому что значения необязательных элементов можно не указывать. В этом случае будут использоваться значения по умолчанию.
Однако никто не запрещает указать своё значение вместо дефолтного. В примере выше каналу NationalGeographicChannel мы установили не равный единице элемент level, а ещё задали для обоих классов непустые множества тегов — хоть это и необязательный элемент.
Кроме элементов со значениями по умолчанию, в аннотациях можно использовать константы. Они, как и в интерфейсах, неявно public, static и final. Вот, к примеру, аннотация для описания времени на чтение каналов, в которой заданы константы для минимального и максимального тайминга:
Шаг четвёртый
Определяем область действия
К чему применима аннотация
До этого шага мы использовали аннотации только для классов, но они применимы к интерфейсам, методам, параметрам класса, локальным переменным и не только. За область применимости аннотации отвечает другая аннотация — @Target. Полный список доступных значений для её единственного элемента value есть в официальной документации.
Если не задавать @Target, аннотацию можно использовать для любых программных элементов.
Мы же для примера напишем аннотацию, которая будет применима к методам и конструкторам классов. Предположим, она будет показывать, что после выполнения таких конструкторов и таких методов нужно будет отправить кому-то тревожное сообщение. Реализацию обработчика оставим за кадром, а аннотация будет выглядеть так:
Как использовать:
Если теперь попробуем написать @Alarm перед названием самого класса SecureChannel, получим ошибку компиляции, потому что в @Target не включено значение для типа элемента «класс».
Когда аннотация доступна
Если мы и правда хотим прямо во время выполнения программы искать какие-то помеченные аннотацией @Alarm методы, одним только указанием @Target не обойтись.
Есть ещё одна аннотация для описания аннотаций — это @Retention. Она определяет доступность в течение жизненного цикла программы. У её единственного элемента value всего три доступных значения:
Значение | Описание |
---|---|
RetentionPolicy.SOURCE | Аннотация останется только в файлах исходников, в .class-файлы сведения о ней не попадут |
RetentionPolicy.CLASS — значение по умолчанию | Аннотация будет сохранена в .class-файлах, но не будет доступна во время выполнения программы |
RetentionPolicy.RUNTIME | Аннотация будет сохранена в .class-файлах, доступна во время выполнения программы |
Воспользуемся новыми знаниями и допишем нашу тревожную аннотацию:
Теперь она будет доступна в рантайме, что и требовалось.
Шаг пятый
Делаем аннотацию повторяющейся
Вернёмся к аннотации для описания хобби и зададимся вопросом: что, если в канале попадается информация о нескольких увлечениях? Например, в нём публикуются обзоры новых фильмов и афиши концертов — вроде и про кино, и про музыку.
Попробуем пометить класс такого универсального канала сразу двумя @Hobby с разными значениями элементов:
Иии… получим ошибку компиляции! Вот такую:
Что ж, сделаем её repeatable, то есть повторяющейся. Для этого опять понадобится аннотация — @Repeatable. Пометим ею @Hobby:
Но снова натыкаемся на возражения компилятора: оказывается, у @Repeatable должен быть указан обязательный элемент, а тип этого элемента — ещё одна аннотация 😵
Глубоко выдохнем и создадим её. В этой очередной аннотации нужно указать, какую другую аннотацию — в нашем случае @Hobby — мы собираемся повторять:
И последний шажок — укажем класс этой новой аннотации в качестве значения для @Repeatable:
Теперь, наконец, всё скомпилируется, и @Hobby можно будет повторять для одного класса сколько угодно раз.
Подытожим
Чтобы вам было ещё проще создавать свои аннотации на Java, мы подготовили две схемы, в которых собрали правила синтаксиса для их объявления и использования.
Узнайте больше об аннотациях и других элементах языка Java на нашем курсе «Профессия Java-разработчик». Вы научитесь программировать на самом востребованном языке и сможете устроиться на высокооплачиваемую работу.