Створення анотацій – процес доволі простий, хоч і обмежений деякими правилами. Тепер нам потрібно розібратися, у чому полягає їхня користь на практиці.
Давай пригадаємо, як ми створюємо свою анотацію.
Пишемо анотацію, яка буде анотувати класи та методи й містити інформацію про автора й версію:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Info {
String author() default "Author";
String version() default "0.0";
}
Наші класи, до яких ми додали анотацію:
@Info
public class MyClass1 {
@Info
public void myClassMethod() {}
}
@Info(version = "2.0")
public class MyClass2 {
@Info(author = "Anonymous")
public void myClassMethod() {}
}
@Info(author = "Anonymous", version = "2.0")
public class MyClass3 {
@Info(author = "Anonymous", version = "4.0")
public void myClassMethod() {}
}
Як нам скористатися цими даними на етапі роботи програми?
Витягти метадані з анотацій можна за допомогою рефлексії. Згадаймо, що таке рефлексія. Це механізм дослідження даних про програму під час виконання. Рефлексія дозволяє отримувати інформацію про поля, методи, конструктори класів, а також про класи.
За допомогою рефлексії ми прочитаємо анотації у класі та виведемо необхідну нам інформацію.
Розпізнаємо дані з наших класів у main:
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) throws NoSuchMethodException {
readMyClass(MyClass1.class);
readMyClass(MyClass2.class);
readMyClass(MyClass3.class);
}
static void readMyClass(Class<?> myClassObj) throws NoSuchMethodException {
System.out.println("\nКлас " + myClassObj.getName());
readAnnotation(myClassObj);
Method method = myClassObj.getMethod("myClassMethod");
readAnnotation(method);
}
static void readAnnotation(AnnotatedElement element) {
try {
System.out.println("Пошук анотацій в " + element.getClass().getName());
Annotation[] annotations = element.getAnnotations();
for (Annotation annotation : annotations) {
if (annotation instanceof Info) {
final Info fileInfo = (Info) annotation;
System.out.println("Автор: " + fileInfo.author());
System.out.println("Версія: " + fileInfo.version());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
До методу readMyClass ми передаємо екземпляр нашого класу на обробку.
Далі до методу readAnnotation ми можемо передати і клас, і метод. Так і зробимо: передаємо туди об'єкт класу та об'єкт методу. Приймається об'єкт, який реалізує контракт AnnotatedElement. Це дозволяє дістати з нього список анотацій та прочитати інформацію щодо кожної з них.
Зверни увагу на те, що ми не дістанемо інформацію, не перевіривши належність анотації до нашого типу анотації (if (annotation instanceof Info)).
На виході ми отримуємо повну інформацію з анотацій:
Пошук анотацій в java.lang.Class
Автор: Author
Версія: 0.0
Пошук анотацій в java.lang.reflect.Method
Автор: Author
Версія: 0.0
Клас annotation.MyClass2
Пошук анотацій в java.lang.Class
Автор: Author
Версія: 2.0
Пошук анотацій в java.lang.reflect.Method
Автор: Anonymous
Версія: 0.0
Клас annotation.MyClass3
Пошук анотацій в java.lang.Class
Автор: Anonymous
Версія: 2.0
Пошук анотацій в java.lang.reflect.Method
Автор: Anonymous
Версія: 4.0
Так за допомогою рефлексії ми змогли дістати метаінформацію.
Тепер розглянемо приклад використання анотацій для вдосконалення коду, зокрема підвищення легкості читання, швидкості роботи та загальної якості. У цьому нам допоможе Lombok.
Lombok — це плагін-надбудова компілятора, який за допомогою ключових слів-анотацій приховує величезну кількість коду і таким чином розширє мову, спрощує розробку та додає деяку функціональність.
Розглянемо приклад анотацій з Lombok:
@ToString | Генерує реалізацію для метода toString(), яка складається з акуратного представлення об'єкта: імені класу, всіх полів та їхнього значення. |
|
@EqualsAndHashCode | Генерує реалізації equals та hashCode, які за замовчуванням використовують нестатичні та нестаціонарні поля, але налаштовуються. | Детальніше можна прочитати на сайті проєкту. Там описано приклад із використанням @EqualsAndHashCode і без нього, зі стандартною реалізацією. |
@Getter / @Setter | Генерує гетери та сетери для приватних полів. |
|
@NonNull | Використовуються для твердження, що поля не є null під час створення екземпляра об'єкта. Інакше викидається виняток NullPointerException. |
|
У Lombok є ще багато корисних анотацій, які використовуються рідше. Ми розглянули просту частину його анотацій. Про сам проєкт можна прочитати більше на офіційному сайті.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ