Главное что нужно знать об аннотациях — они ничего не делают! Аннотации представляют собой простые метки в коде и больше ничего. Когда кто-то говорит «Аннотация @DoSomething делает блаблабла» это фактически означает, что кто-то где-то вызывает код, который находит типы с этой аннотацией и делает блаблабла.
Зачем нужны аннотации? Они позволяют разработчику декларировать дополнительные свойства кода, который он пишет. Говоря простыми словами — когда разработчик пишет над определением метода @Test это означает две вещи:
- Разработчик говорит себе и всему миру: «Этот метод не предназначен для использования в коде, этот метод реализует какую-то проверку и должен вызываться только во время исполнения тестов»
- Тестовый фреймворк осматривает все доступные ему методы в тестовых классах и запускает все методы, которые имеют аннотацию @Test
Другой хороший пример пользы от аннотаций был в статье, рассказывающей о разрешении конфликтов при внедрении зависимостей: некоторой аннотацией (можно считать, что именем) помечался как внедряемый класс, так и переменные, в которые он внедрялся.
Подготовка
Нам понадобится пустой maven проект с JUnit и Hamcrest:
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <junit.version>4.12</junit.version> <hamcrest.version>1.3</hamcrest.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-library</artifactId> <version>${hamcrest.version}</version> <scope>test</scope> </dependency> </dependencies>
И класс приветствия:
public class Greeter { public String greet(String target) { return "Hello, " + target; } }
public class GreeterTest { @Test public void testGreet() throws Exception { Greeter testedObject = new Greeter(); assertThat(testedObject.greet("TEST"), is("Hello, TEST")); } }
Собственная аннотация
Сделать аннотацию самому не сложно — аннотация состоит из имени, свойств и конфигурации:
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface GreeterTarget { String value() default "world"; }
@Target(ElementType.FIELD) говорит, что эта аннотация может быть применена только к полям классов. Аннотации можно применять к пакетам, классам и их конструкторам, методам и их аргументам, переменным и так далее. @Retention(RetentionPolicy.RUNTIME) делает аннотацию доступной во время исполнения, а не удаляет её во время компиляции.
@interface говорит, что это аннотация, а не обычный интерфейс. Внутри аннотации могут быть определены её свойства, но их определение несколько необычно: во первых они выглядят как методы, во вторых у них может быть значение по умолчанию.
Использование аннотации очевидно:
@GreeterTarget private static Greeter world = new Greeter(); @GreeterTarget(value = "annotations") private static Greeter annotations = new Greeter(); @GreeterTarget("Java") private static Greeter java = new Greeter();
В первом случае значением свойства value будет значение по умолчанию: «world», во втором «annotations», а в третьем … таки да, «Java». У свойства по имено value() если удобное соглашение, именно в него попадает значение аннотации по умолчанию.
Использовать аннотацию гораздо сложнее. Так как аннотации относятся к коду, а не к данным программы, то для доступа к ним приходиться использовать reflection:
Вначале надо получить аннотируемый объект, потом получить из него аннотацию и, наконец, взять у аннотации её значения. Другой пример показывает, что никакой другой связи между кодом и аннотациями нет:
Field javaField = Main.class.getDeclaredField("java"); GreeterTarget javaTarget = javaField.getAnnotation(GreeterTarget.class); System.out.println(world.greet(javaTarget.value()));