Аннотации Java — основы
Если вы когда-либо видели кодовую базу Java, есть большая вероятность, что вы видели и что-то вроде @Override или подобного перед методом или классом. Такие тэги называются аннотациями. Аннотации — это тэги метаданных, которые помогают определить дополнительную информацию для классов, интерфейсов, методов или полей. Аннотации не добавляют дополнительной реализации к функциональному коду, но помогают с необязательной информацией, такой как:
- Информация для компилятора. Компилятор может использовать аннотации для обнаружения ошибок или подавления предупреждений.
- Информация для разработчика. Когда кто-то просматривает код, аннотации помогают сделать его читабельным и более легким для понимания.
- Обработка времени компиляции и развертывания. Программные средства могут обрабатывать аннотации для создания кода, XML-файлов и т.д.
- Обработка во время выполнения. Аннотации могут проверяться во время выполнения для прохождения тестов.
Java содержит целый набор предопределенных аннотаций и даже позволяет определять свои собственные. Ниже приведем список наиболее широко распространенных и важных аннотаций.
Предопределенные аннотации
@Override
Аннотация @Override сообщает компилятору, что элемент подкласса переопределяет элемент родительского класса или суперкласса. Хотя эта аннотация не обязательна при переопределении метода, она помогает предотвратить ошибки. Если метод, помеченный как “override”, не может правильно переопределить метод суперкласса, компилятор выдает ошибку.
class Avatar <
public void destroyEvil() < . >// переопределенный
>class Aang extends Avatar <
@Override
// overriding method
public void destroyEvil() < . >
>
@SuppressWarnings
Предупреждения компилятора полезны, если вы их читаете, но они часто создают “шум” в терминале. @SuppressWarnings будет подавлять эти предупреждения. Компилятор Java может выдавать множество предупреждений, но с помощью @SuppressWarnings вы можете скрыть все предупреждения или выбрать предупреждения, появления которых вам хочется избежать.
@SuppressWarnings() будет скрывать предупреждения о непроверенных (unchecked) и устаревших (deprecated) методах.
@SuppressWarnings("unchecked")
void uncheckedWarning() <
List words = new ArrayList();
words.add("hello"); //выбрасывает предупреждение о непроверенном
>
@deprecated
Обозначение @deprecated также очень распространено. Это означает, что аннотированный метод устарел и больше не поддерживается разработчиками. Компилятор не будет обрабатывать устаревший метод иначе, чем обычный метод. Таким образом, даже если метод допустимо вызвать, возвращаемый им ответ может быть не идеальным. Это документация для разработчиков.
@depricated
public String prepareForY2K()
@author
Тэг @author — простая аннотация, документирующая автора метода или файла. Обычно соединяется с некоторой дополнительной информацией, такой как версия, номер релиза и т.д.
Oracle рекомендует писать тэги в следующем порядке:
- @author — документирует автора кода;
- @version — обеспечивает только одно обновление за раз (позволяет избежать блокировки);
- @param — документирует имя и описание параметров;
- @return — документирует возвращаемое значение; опускать, если возвращает пустоту (void);
- @throws — документирует проверенные исключения (объявленные в throws );
- @see — ссылка или указание на ссылку;
- @since — документирует версию продукта, с которой была добавлена новая функциональность;
- @deprecated — документирует, что код больше не поддерживается.
Тест-аннотации
Написание тестов — важнейший аспект цикла разработки и так же (если не более) важно, как и написание самой кодовой базы. Существуют различные аннотации, созданные явно для тестов.
@Test
@Test сообщает JUnit, что аннотированный метод должен выполняться как тест. Чтобы запустить метод, JUnit создает новый экземпляр класса, а затем вызывает тестовый метод.
Для этой аннотации можно указать два необязательных параметра:
@Timeout приводит к сбою метода тестирования, если выполнение занимает больше времени, чем указанное время, измеренное на часах в миллисекундах.
Например, следующий тест упадет (через 0,1 секунды):
@Test(timeout=100)
public void toInfinityAndBeyond() <
while(true);
>
@Expected объявляет, что тестовый метод должен выдавать определенное исключение, в противном случае тест завершится неудачей.
Например, в примере ниже произойдет сбой:
@Test(expected=NullPointerException.class)
public void outOfBounds() <
new ArrayList().get(1);
>
@Ignore
Аннотация @Ignore указывает игнорировать тест или группу тестов, чтобы избежать потенциального сбоя при выполнении.
Игнорировать тесты можно в двух сценариях:
- игнорировать тестовый метод, помеченный @Test .
- игнорировать все тесты на уровне класса.
@Ignore
@Test(expected=NullPointerException.class) //obviously wrong test
public void outOfBounds() <
new ArrayList().get(1);
>
@Before
Методы, помеченные тэгом @Before , выполняются перед каждым тестом. Это полезно, когда вы хотите выполнить некоторый код перед запуском теста, например, настроить тестовую среду. В Junit5 @Before был переименован в @beforeEach , что также работает.
Родственная аннотация @beforeAll или @BeforeClass используется, когда перед серией тестов необходимо выполнить дорогостоящую операцию, такую как запуск сервера или внесение изменений в базу данных.
@After
@After — это противоположность предыдущему тэгу. Все методы, помеченные @After , будут запущены после теста.
Методы @AfterAll или @AfterClass выполняются после выполнения всех тестов класса.
Все методы, аннотированные @beforeAll и @afterAll должны быть статическими, так как выполняются перед запуском тестов класса.
Однако методы @Before и @After не должны быть статическими, иначе компилятор выдаст ошибку.
public class OutputFileTest < @BeforeAll
public static void startServer() @Before
public void createTestLogFile() < . >
@After
public void deleteTestLogFile() < . >
@Test
public void test1() @Test
public void test2() @AfterAll
public static void stopServer() < . >
>
Код, приведенный выше, выполнится в следующем порядке:
0️⃣ startServer() 1️⃣ createTestLogFile() 2️⃣ test1() 3️⃣ deleteTestLogFile() 4️⃣ createTestLogFile() 5️⃣ test2() 6️⃣ deleteTestLogFile() 7️⃣ stopServer()
Этот список далеко не исчерпывающий, но он охватывает самые основные аннотации. Воспользуйтесь приведенными выше примерами, чтобы получить представление о лучших практиках в программировании на Java.
- Когда стоит использовать перечисления в Java?
- Java-библиотеки, которые повысят вашу производительность
- Как правильно учиться Java-программированию: история одного тьютора
Аннотации в Java и override

Аннотации в Java – это специальные формы синтаксических метаданных. Аннотация может быть добавлена в исходный код приложения. Используется для:
- анализа кода;
- компиляции;
- выполнения приложения.
Аннотации могут быть присвоены пакетам, классам, методам, параметрам и переменным. Обладают элементарной формой представления и являются своеобразными тегами. Они служат для определения дополнительной информации. К функциональности никакого отношения не имеют.
Форма представления – @НазваниеАннотации.
Далее будут рассмотрены все аннотации в Java. Огромной популярностью среди них пользуется override.
С чем помогают
Аннотации в Джаве не имеют функциональности, зато помогают с необязательными данными:
- Сведения для компиляторов. Они могут использовать аннотации для того, чтобы обнаруживать ошибки или подавлять предупреждения.
- Данные для разработчиков. В этом случае annotations – это своеобразные подсказки. Они делают исходный код более понятным и читабельным.
- Обработка времени компиляции и развертывания. Программные средства смогут обработать рассматриваемый компонент для формирования кодов, XML-документов и так далее.
- Обработка времени выполнения. Annotations могут быть проверены во время выполнения для прохождения тестов.
Аннотации в Java – это целый набор предопределенных объектов. В данном языке разработки можно создавать их самостоятельно. Далее будут представлены наиболее распространенные и важные варианты.
SuppressWarnings
Предупреждения компилятора будут полезны, если разработчик их читает. Только они нередко создают дополнительный «шум» в терминале. При помощи @SuppressWarnings удается подавлять соответствующие предупреждения. Их можно убрать полностью или выбрать конкретные «сообщения», которых хочется избежать.
Здесь первая строка отвечает за сокрытие предупреждения о непроверенных методах (unchecked). Можно также воспользоваться удалением «сообщений» об устаревших методах. Тогда в приведенном примере необходимо заменить unchecked на deprecated.
Deprecated
@deprecated указывает на то, что аннотируемый метод устарел и не поддерживается разработчиками. Компилятор не будет его обрабатывать иначе, чем обычный метод. Возвращаемый ответ здесь не идеален. Это документация непосредственно для разработчиков программного обеспечения.
Author
Еще один простой вариант. Означает документацию автора метода или документа. Чаще всего соединяется с дополнительными сведениями. Пример – версия программы, номер релиза и так далее.
В Oracle теги пишутся в следующем порядке:
- author – документация автора кода;
- version – обеспечение только одного обновления за раз, позволяя миновать блокировки;
- param – документация имени и описание имеющихся параметров;
- return – «справка» о возвращаемом значении;
- throws – документирование проверенных исключений;
- see – ссылка или указание на нее;
- since – версия приложения, в которой были добавлены новые возможности и операции;
- deprecated – указание на то, что код перестал поддерживаться разработчиками.
Такой порядок делает программный код более понятным и читабельным. Это стандарт, о котором необходимо помнить каждому разработчику.
Override
Аннотация @override означает «маркер». Она применяется только к методам. Данная аннотация указывает компилятору на то, что компонент подкласса переопределяет элемент родительского класса или суперкласса. Она не является обязательной. При переопределении метода помогает избежать ошибок.
Метод с @override будет «инициализировать» метод суперкласса. Если в родительском классе или интерфейсе соответствующий элемент кода не обнаружен, на экране будет появляться ошибка компиляции или интерпретации. Аннотированный метод переопределяет метод суперкласса.
class Avatar < public static void destroyEvil() < … >// переопределенный > class Aang extends Avatar < @Override // overriding method public void destroyEvil() < … >>
Выше – форма примера кода, который позволяет лучше разобраться в этой аннотации. А вот как он будет выглядеть в редакторе:
Кроме предопределенных аннотаций в Java есть тест-аннотации.
Тест-аннотации
Формирование тестов – важный этап жизненного цикла разработки. Написание самой кодовой базы тоже требует определенного внимания. В Java имеются аннотации, используемые явно для тестов.
Test
Сообщить JUnit, что аннотированный метод должен обрабатываться в качестве теста. Для его запуска создается новый экземпляр класса, а затем происходит непосредственный вызов тестового метода.
Здесь присутствуют два параметра:
- Timeout – приводит к сбою метода тестирования, если на его выполнение затрачивается больше указанного времени. Измерения – миллисекунды.
- Expected – означает, что тестовый метод должен выдавать то или иное исключение. В противном случае тестирование завершается неудачей.
Эти параметры не являются обязательными. Устанавливаются программистом только в случае необходимости.
Ignore
Запись, обозначающая необходимость игнорирования теста или их группу. Это нужно для того, чтобы избежать потенциального сбоя функционирования программы. Применяется в нескольких сценариях: если метод помечен @Test, а также при необходимости игнорирования всех тестов на уровне класса.
Before
@Before значит, что все помеченные таким тегом методы будут выполняться перед каждым тестом. Есть схожая аннотация – @beforeAll. Она применяется, когда перед серией тестов требуется выполнить дорогостоящую операцию. Пример – запуск сервера или корректировка имеющейся базы данных.
After
Противоположность предыдущему варианту. Позволяет не переопределить методы, которые выполняются до тестирования, а указывает на то, что должно быть запущено после активации теста. @afterAll и @afterClass будут выполняться после всех тестов класса. Все они должны быть статическими.
Аннотации — Java: Корпоративные приложения на Spring Boot
В Java аннотации встречаются часто, но особенно много их в Spring Boot. Чтобы понять, как работает фреймворк, нужно разобраться в устройстве аннотаций. В этом уроке мы познакомимся с ними и узнаем, как они работают.
Аннотации — это механизм со своим синтаксисом, который позволяет добавлять метаданные в код. Например, так мы можем добавить какую-то дополнительную информацию, которую затем можно прочитать из исходного кода class-файлов или получить в рантайме — то есть во время работы программы. Сами по себе аннотации на код никак не влияют, в этом смысле они похожи на комментарии. Все действия происходят в коде, который ищет аннотации и на их основе меняет поведение.
Аннотации можно указывать на разных уровнях кода. Сюда входят классы, методы и параметры:
package io.hexlet.spring; import org.springframework.boot.SpringApplication; // Определения аннотаций — это обычный код, который нужно импортировать import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; @RestController // Аннотация на уровне класса @RequestMapping("/api") // Аннотация на уровне класса @SpringBootApplication public class Application @Autowired // Аннотация на уровне поля private UserRepository userRepository; public static void main(String[] args) SpringApplication.run(Application.class, args); > @GetMapping("/hello") // Аннотация на уровне метода public String sayHello(@RequestParam(name = "name", required = false, defaultValue = "Guest") String name) // Аннотация на уровне параметров return "Hello, " + name + "!"; > @PostMapping("/greet") // Аннотация на уровне метода public String greet(@RequestBody String name) // Аннотация на уровне параметров return "Greetings, " + name + "!"; > >
Некоторые аннотации выглядят как метка — @RestController , другие похожи на вызов метода с параметрами — @RequestMapping(«/api») . Принцип работы от этого не меняется: аннотация не превращается в вызов метода, она остается меткой с дополнительными данными.
Зачем аннотации вообще нужны? Во-первых, они значительно сокращают объем шаблонного кода — это повторяющийся одинаковый код, который нужен для конфигурации приложения, соединения его частей друг с другом или других задач. Раньше ту же задачу решали с помощью конфигурационных XML-файлов, которые иногда были просто огромными. Из-за этого Java-программистов часто называли XML-программистами. Активное использование аннотаций существенно упростило этот процесс.
Встроенные аннотации
Подавляющее большинство аннотаций в реальных проектах написаны разработчиками библиотек, а еще буквально несколько аннотаций встроено прямо в Java. Например, аннотация @Deprecated позволяет отметить класс или метод как устаревший. Эту информацию затем можно увидеть в подсказках редактора. Такая аннотация помогает другим программистам при выборе классов и методов для реализации их задач:
@Deprecated public void oldMethod() // Дальше продолжается какой-то код >
Самая часто используемая аннотация — это @Override . Она указывает, что помеченный метод должен переопределять метод наследуемого класса или реализовывать метод интерфейса. Сама аннотация не обязательна при переопределении, но она помогает избежать ошибок и сделать код проще для чтения:
// Пример из Spring Boot public class User implements UserDetails @Override public boolean isAccountNonExpired() return true; > @Override public boolean isAccountNonLocked() return true; > @Override public boolean isCredentialsNonExpired() return true; > >
Кастомные аннотации
С такими аннотациями мы будем встречаться чаще всего. Изучить их работу заранее невозможно — каждая конкретная аннотация обрабатывается своим образом и приводит к своим последствиям. Причем в большинстве случаев программист до конца не знает, что на самом деле происходит внутри.
С одной стороны, это хорошо — можно сфокусироваться на важном. С другой стороны, из-за этого код начинает работать как магия, и это может стать проблемой.
Изучим пример типичного контроллера на Spring Boot. Здесь можно насчитать около десятка аннотаций, причем из разных пакетов:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import jakarta.validation.Valid; @RestController @AllArgsConstructor @RequestMapping("/api") public class PostsController @Autowired private final PostRepository repository; @PostMapping("/posts") @ResponseStatus(HttpStatus.CREATED) PostDTO create(@Valid @RequestBody PostDTO postData) throws JsonProcessingException // Тут логика > @GetMapping("/posts/") @ResponseStatus(HttpStatus.OK) PostDTO show(@PathVariable Long id) // Тут логика > >
Как классы или интерфейсы, аннотации тоже имеют свое определение, поэтому их необходимо импортировать. Редактор делает это самостоятельно, поэтому тут проблем возникнуть не должно.
Устройство кастомных аннотаций
Посмотрим, как определять и обрабатывать аннотации. Эти знания помогут разобраться в принципе работы аннотаций, а еще вопросы на эту тему часто задают на собеседованиях. Напишем аннотацию @LogExecutionTime , которая замеряет время выполнения помеченного ей метода:
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface LogExecutionTime >
Иронично, что определение аннотации само помечено ими. В коде выше мы видим три обязательные аннотации:
- @interface определяет саму аннотацию
- @Retention определяет жизненный цикл аннотации, то есть указывает, как долго аннотация должна оставаться с кодом. В этом случае аннотация должна быть доступна в рантайме, потому что именно так мы будем ее обрабатывать
- @Target определяет, где мы будем применять аннотацию (например, в методах)
Аннотация готова, можно начинать применять ее. При этом в работе кода ничего не поменяется, потому что обработчик еще не написан:
// Мы должны импортировать нашу аннотацию import путь>.LogExecutionTime; public class SomeService @LogExecutionTime public void serve() throws InterruptedException Thread.sleep(1500); // Выполняем какую-то задачу > public void anotherMethod() // Этот метод еще не отмечен аннотацией, // поэтому время выполнения метода не измеряется и не логируется > >
Дальше мы напишем обработчик аннотации. Это обычный Java-код, поэтому нужно убедиться, что он выполняется до того, как исполнение дойдет до кода с аннотациями. В нашем случае обработчик выполняется в методе main() :
import java.lang.reflect.Method; public class Main public static void main(String[] args) var service = new SomeService(); // Итерируем все методы класса for (Method method : SomeService.class.getDeclaredMethods()) // Проверяем, есть ли у метода аннотация @LogExecutionTime if (method.isAnnotationPresent(LogExecutionTime.class)) var startTime = System.currentTimeMillis(); try // Выполняем метод с аннотацией LogExecutionTime method.invoke(service); > catch (Exception e) e.printStackTrace(); > long endTime = System.currentTimeMillis(); long executionTime = endTime - startTime; System.out.println("Executed method: " + method.getName()); System.out.println("Execution time: " + executionTime + " milliseconds"); > > > >
Здесь мы видим рефлексию — технику, которая отображает информацию о программе во время ее работы. Чтобы использовать ее, мы берем все методы класса SomeService , находим методы с аннотацией LogExecutionTime и вызываем их методы, проверяя время выполнения.
Параметры кастомных аннотаций
Добавим в @LogExecutionTime два параметра. Первый временно выключит логирование, а второй задаст минимальное время выполнения, ниже которого логировать не нужно:
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface LogExecutionTime boolean enabled(); long threshold() default 0; // Пороговое время в миллисекундах >
Параметры описываются внутри тела аннотации определенным способом. Он похож на определение методов с отсутствующим телом и возможностью указать значение по умолчанию. Кстати, значение по умолчанию можно и не прописывать. В таком случае компилятор потребует указать его при добавлении аннотации:
public class SomeService @LogExecutionTime(enabled = true) public void serve() throws InterruptedException Thread.sleep(1500); // Выполняем какую-то задачу > @LogExecutionTime(enabled = true, threshold = 100) public void anotherMethod()
Открыть доступ
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно
- 130 курсов, 2000+ часов теории
- 1000 практических заданий в браузере
- 360 000 студентов
Наши выпускники работают в компаниях:
Аннотации в Java. Не путать с комментариями

Аннотации – это пометки, с помощью которых программист указывает компилятору Java и средствам разработки, что делать с участками кода помимо исполнения программы. Аннотировать можно переменные, параметры, классы, пакеты. Можно писать свои аннотации или использовать стандартные – встроенные в Джава.
Вы узнаете аннотацию по символу @ в начале имени: @Override – стандартная аннотация Javа, которая предупреждает, что ниже мы что-то переопределим:
class SomeClass < void method() < System.out.println("Работает метод родительского класса."); > > class AnotherClass extends SomeClass < // наследуем методы SomeClass в новом классе @Override void method() < // переопределяем метод System.out.println("Работает метод класса-потомка."); > >
Если в имени метода из AnotherClass будет опечатка, компилятор учтет @Override и выдаст ошибку. Без аннотации он не заметил бы подвоха и безропотно создал бы новый метод в дополнение к method из SomeClass.
Обратите внимание, сама аннотация никак не влияет на переопределение метода, но позволяет контролировать успешность переопределения при компиляции или сборке. Мы защитили участок кода от неприметной ошибки, на поиск которой в большой программе ушли бы часы. Это лишь одно из многих применений аннотаций.
Зачем нужны аннотации Java
- автоматически создавать конфигурационные XML-файлы и дополнительный Java-код на основе исходного аннотированного кода;
- документировать приложения и базы данных параллельно с их разработкой;
- проектировать классы без применения маркерных интерфейсов;
- быстрее подключать зависимости к программным компонентам;
- выявлять ошибки, незаметные компилятору;
- решать другие задачи по усмотрению программиста.
Поясним понятие «маркерный интерфейс». Интерфейсы без каких-либо методов действуют как маркеры. Они лишь говорят компилятору, что объекты классов, которые имплементируют такой интерфейс без методов, должны иметь отличительные черты, восприниматься иначе. Например, java.io.Serializable, java.lang.Cloneable, java.util.EventListener. Маркерные интерфейсы ещё известны как «теги» — они добавляют общий тег ко всем унаследованным классам и объединяют их в одну категорию.
При первом появлении в Java EE 5 аннотации были представлены как инструмент, который ускоряет разработку больших web-сервисов и клиентских приложений. Как это работает?
Обработка аннотации в Джава
На основе аннотаций компилятор может с помощью специальных обработчиков генерировать новый код и файлы конфигурации.
Обработчиками обычно выступают библиотеки и утилиты, которые можно брать у сторонних авторов (или создавать самостоятельно) и прикреплять к проекту в среде разработки. Способ подключения зависит от IDE или системы сборки. В Maven обработчики подключают с помощью модуля annotation-user или плагина maven-compiler-plugin.
Парсинг аннотаций происходит циклически. Компилятор ищет их в пользовательском коде и выбирает подходящие обработчики. Если вызванный обработчик на основе аннотации создаёт новые файлы с кодом, начинается следующий этап, где исходным материалом становится сгенерированный код. Так продолжается до тех пор, пока не будут созданы все необходимые файлы.
Пишем первую аннотацию на Java
Допустим, у нас есть веб-сервис, который поддерживает несколько версий одного функционала для соблюдения совместимости. И есть обработчик аннотаций, который позволяет компилятору выбирать нужные версии. На минутку забудем о существовании Git 🙂
Где хранить данные о версии и авторе функционала? Конечно же, в аннотации. Напишем её. Новую аннотацию объявляют с помощью ключевого слова @interface:
public class SomeClass < public @interface version < private float v(); // номер версии private String author() default “Аноним"; // автор > // остальное содержимое класса >
Это немного искусственный, но зато простой и наглядный пример аннотации на Java. Мы добавили два атрибута, которые выглядят как методы. Отличие в том, что при объявлении атрибутов никогда не используют оператор throws и не назначают параметров. Значениями могут выступать:
- примитивные типы Java,
- классы или снабженные параметрами обращения к классам,
- перечисления,
- другие аннотации,
- массивы из вышеперечисленных элементов.
Можно указывать значения по умолчанию, что мы и сделали выше с полем author. При постановке аннотации атрибуты с дефолтными значениями можно пропускать.
@version(v=1.0f); // автор остаётся “Анонимом”, а "f" после числа ставим для явного указания на тип float SomeClass < // . >
Чтобы нашу аннотацию использовали только по назначению, вернёмся к её объявлению и укажем, где и когда она должна работать:
@Target(ElementType.TYPE) /* Аннотация применима только к классам, а не к пакетам, отдельным методам, переменным и т.д. */ @Retention(RetentionPolicy.RUNTIME) /* Применяется во время выполнения программы. Если бы нам нужно было применять аннотацию к исходному коду на этапе компиляции программы, мы бы указали RetentionPolicy.SOURCE.*/ public @interface version < // . >
Остается ассоциировать аннотацию с нужным классом и запустить программу.
Используем аннотации Java для сравнения баз данных
Напишем что-нибудь более сложное и полезное. Нам нужно свести несколько БД в одну. Для начала сравним их, чтобы найти одинаковые поля и значения, устранить дубли и внести новую информацию из каждого источника. Могут возникнуть и сопутствующие задачи: одни значения потребуется отформатировать, другие — проигнорировать, всё вместе вывести в виде .xls-отчёта.
Мы наметили конфигурацию работы с данными, остаётся её реализовать. Вот здесь на сцену выходят аннотации. С их помощью мы можем задать (а если потребуется — и скорректировать) способы обработки каждого поля. При этом конфигурация будет выглядеть абсолютно прозрачно: читатель кода сразу поймёт, что к чему применено. И ход исполнения программы не будет затронут. Волшебно! Пишем:
// Сначала укажем, что аннотацию надо применять на уровне ПОЛЯ @Target(ElementType.FIELD) // Использовать аннотацию надо во время выполнения программы @Retention(RetentionPolicy.RUNTIME) public @interface FieldInspector < //создаём аннотацию, придумываем ей имя // Выбираем, сравнивать ли источники. По умолчанию — да. boolean compareSources() default true; /* Получаем формат значения в отчёте, а если не получаем — подставляем формат по умолчанию — native. */ СhooseTheFormat displayFormat() default СhooseTheFormat.NATIVE; // Получаем ID текущего поля для сопоставления в нескольких базах int id(); // Уточняем, какое имя использовать для поля в таблице. String field_name() default ""; // Какие источники сравнивать — формируем список. TheSource[] sourcesToCompare() default <>; >
Теперь ассоциируем аннотацию с полем. Допустим, у нас в БД книги:
@FieldInspector(id = 2, field_name = "TITLE") private String book_title; // переменная хранит название книги
Присвоили id, присвоили имя полю в отчёте. Сверка значений со всеми базами запустится по умолчанию, т.к мы не указали для compareSources значение false. А если мы заведомо знаем, что искать нужно не во всех БД, а в конкретных? Например, сюжет есть не у любой книги. Задаём источники вручную:
// проверять только пьесы и беллетристику @FieldInspector(id = 3, field_name = "PLOT" sourcesToCompare =TheSource.PLAYS, TheSource.FICTION>) private String plot;
Если у нас есть поле «Примечания», значение которого не нужно сверять, мы можем перед переменной для хранения примечаний поставить такую аннотацию:
@FieldInspector(id = 300, field_name = "NOTES", compareSources = false) private String notes;
Неплохо, но как сделать, чтобы правила обработки выбирались в зависимости от значения поля? Можно связать правила с классом обработчика, чтобы тот выбирал, как поступать с каждым полем. Для этого потребуется ещё одна короткая аннотация:
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface RuleApplier < // массив строк, где хранятся параметры обработчика, по умолчанию — параметров нет String[] parameters() default <>; // класс, который отвечает за применение правил к каждому источнику Class?> processor() default MyRuleApplier.class; >
Проверим, как это работает. Давайте не сравнивать данные о литературе, которая поступила в библиотеку до 1990 года — допустим, эти данные у нас были только в одной базе:
@FieldInspector(id = 4, label = "THE YEAR OBTAINED", displayFormat = СhooseTheFormat.YEAR_FORMAT>) @RuleApplier(processor = IgnoreWhatWasObtainedBefore.class, parameters = < "1990" >) private int book_obtained;
Теперь реагировать на значения менее 1990 в поле «год поступления» мы доверяем конкретному пользовательскому классу. Это даёт нам больше гибкости в работе с данными, но помните, что аннотации — вещь статическая. Какое значение получил обработчик на входе, с тем и разбирается. Интерактива с динамической сменой значений пользователем не получится.