Создание пользовательских аннотаций в Java

В предыдущей статье мы говорили о том, что такое аннотации и как пользоваться готовыми аннотациями в Java. Но Java также дает возможность создавать собственные аннотации для улучшения кода. Чтобы сгенерировать любую пользовательскую аннотацию, необходимо использовать мета-аннотации. В этой статье объясняется, как создавать мета- и пользовательские аннотации и украшать ими объекты. 

Пользовательские аннотации

Наряду со множеством предопределенных аннотаций в Java можно определять и собственные пользовательские аннотации. Самодельные аннотации дают следующие преимущества.

  • Сокращают затраты на написание кода, добавляя к методам поведение по умолчанию.
  • Добавляют настраиваемое поведение классам и интерфейсам.
  • Экономят усилия на написании XML-дескрипторов и интерфейсов-маркеров.

Чтобы определить любую пользовательскую аннотацию, сначала нужно объявить ее с помощью тега @interface. Затем мы определяем цель и область применения через мета-аннотации. Работа с мета-аннотациями (@Retention, @Target, @Inherited) объясняется во второй половине этой статьи. Пользовательские аннотации могут быть определены на трех уровнях:

  • уровень класса;
  • уровень поля;
  • уровень метода.

Уровень класса

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.Type)
public @interface CustomAnnotation{ ... }

Приведенная выше пользовательская аннотация CustomAnnotation создана с политикой доступности во времени выполнения, и ее можно применять ко всем классам. Поскольку у нее нет методов, она служит простым маркером для обозначения нужных классов.

Здесь следует отметить, что любая пользовательская аннотация на уровне класса не может содержать никаких параметров и не должна вызывать никаких исключений. Кроме того, типы возвращаемых значений ограничены примитивами, строками, классами, перечислениями, аннотациями и их массивами, а значение по умолчанию не может быть null.

Уровень поля

Аналогично уровню класса, можно определять аннотации на уровне полей и ограничивать область действия.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface CustomAnnotation{ ... }

Уровень метода

Мы также можем объявлять аннотацию с доступностью во время выполнения, чтобы применять ее к методам классов:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CustomAnnotation{ ... }

Применение пользовательских аннотаций

Вот пример, демонстрирующий использование пользовательских аннотаций.

Речь идет о двух классах: автомобиле Car и двигателе Engine. Предположим, что BasicEngine должен применяться ко всем типам автомобилей. В этом случае можно разработать пользовательскую аннотацию, такую как @BasicEngine, и аннотировать все виды реализаций автомобиля (например, хэтчбеки, спортивные автомобили, седаны и т.д.) через BasicEngine.

Пользовательская аннотация класса (интерфейса):

import java.lang.annotation.*;
@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)

@interface BasicEngine {
String mileage() default "20";
String fuelType() default "Gasoline";
}

Класс, использующий пользовательскую аннотацию (нет необходимости в импорте):

@BasicEngine(bId="30", bName="BioDiesel")
public class Car {
String make;
String model; public Car(String make, String model){
this.make = make;
this.model = model;
}

public void getCarDetails(){
System.out.println("Car Manufacturer: " + make);
System.out.println("Car Model: " + model);
}
}

Класс водителя (Driver) для проверки вышеизложенного:

import java.lang.annotation.Annotation;
public class TestCustomAnnotationBasicEngine {

public static void main(String[] args) throws Exception{
        Car car = new Car("32", "Diesel");
        car.getCarDetails();
        Class carClass = car.getClass();        

Annotation testAnn = carClass.getAnnotation(BasicEngine.class);        

BasicEngine engine = (BasicEngine)testAnn;        

System.out.println("Mileage: " + engine.mileage());
        System.out.println("Fuel Type: " + engine.fuelType());
    }
}

Мета-аннотации

Мета-аннотации  —  это аннотации, применяемые к другим аннотациям для увеличения масштаба. Это очень важно, поскольку позволяет описывать аннотации, используя другие аннотации, и комбинировать аннотации.

В схему языка Java включены следующие важные мета-аннотации.

@Inherited

По умолчанию аннотация не может наследовать от своего суперкласса. Однако, если нужно наследовать аннотацию от суперкласса к подклассу, используется аннотация @Inherited.

@Inherited
public @interface CustomAnnotation { ... }

@CustomAnnotation
public class ParentClass { ... }

public class ChildClass extends ParentClass { ... }

Здесь дочерний класс ChildClass автоматически получает пользовательскую аннотацию CustomAnnotation, поскольку наследуется от родительского класса ParentClass. Дочерний класс может вызывать любую функциональность CustomAnnotation.

@Target

Области действия аннотаций основаны на требованиях метода или файла, таких как конструкторы или объявления. Мы можем ограничить применение аннотации к определенным целям с помощью аннотации @Target.

@Target(ElementType.METHOD)
public @interface CustomAnnotation{ ... }

В приведенном выше коде пользовательская аннотация CustomAnnotation ограничена только методами, т.е. поля, пакеты и т.д. не будут аннотироваться с ее помощью.

Аннотация может быть использована для любого элемента, если целевой тип не определен.

@Retention

Мета-аннотация @Retention указывает уровень, до которого будет доступна аннотация.

Существует три уровня, относительно которых Java позволяет определять политики хранения.

  • RetentionPolicy.SOURCE—  доступно на уровне исходного кода и игнорируется компилятором.
  • RetentionPolicy.CLASS— доступно компилятору во время компиляции и игнорируется JVM.
  • RetentionPolicy.RUNTIME— доступно для JVM.
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAnnotation{ ... }

В заключение, аннотации  —  это удобный инструмент, который предоставляет Java. Их изучение очень помогает в том, чтобы стать более профессиональным разработчиком!

Читайте также:

Читайте нас в TelegramVK и Яндекс.Дзен


Перевод статьи Utkarsh Jain: Java: Creating and Using Custom Annotations

Предыдущая статьяReact: основные ошибки мидл-разработчиков
Следующая статьяВведение в конвейерную обработку данных с использованием бессерверной архитектуры