1. Обзор
В этой статье мы поговорим об основной функции языка Java – аннотациях по умолчанию, доступных в JDK.
2. Что Такое Аннотация
Проще говоря, аннотации-это типы Java, которым предшествует символ”@” .
Java имеет аннотации с момента выпуска версии 1.5. С тех пор они сформировали то, как мы разрабатываем наши приложения.
Spring и Hibernate-отличные примеры фреймворков, которые в значительной степени полагаются на аннотации для использования различных методов проектирования.
В принципе, аннотация присваивает дополнительные метаданные исходному коду, к которому она привязана . Добавив аннотацию к методу, интерфейсу, классу или полю, мы можем:
- Информируйте компилятор о предупреждениях и ошибках
- Манипулирование исходным кодом во время компиляции
- Изменение или проверка поведения во время выполнения
3. Встроенные аннотации Java
Теперь, когда мы рассмотрели основы, давайте взглянем на некоторые аннотации, которые поставляются с ядром Java. Во-первых, есть несколько, которые информируют компиляцию:
- @Переопределение
- @SuppressWarnings
- @Устарело
- @SafeVarargs
- @functional Interface
- @Native
Эти аннотации генерируют или подавляют предупреждения и ошибки компилятора. Их последовательное применение часто является хорошей практикой, так как их добавление может предотвратить будущие ошибки программиста.
Аннотация @Override используется для указания на то, что метод переопределяет или заменяет поведение унаследованного метода.
@SuppressWarnings указывает, что мы хотим игнорировать определенные предупреждения из части кода. Аннотация @SafeVarargs также действует на тип предупреждения, связанного с использованием varargs.
Аннотацию @Deprecated можно использовать для пометки API как больше не предназначенного для использования. Кроме того, эта аннотация была модифицирована в Java 9 для представления дополнительной информации об устаревании.
Более подробную информацию обо всем этом вы можете найти в связанных статьях.
3.1. @Functional Interface
Java 8 позволяет нам писать код более функциональным способом.
Интерфейсы одного абстрактного метода являются большой частью этого. Если мы предполагаем, что один и тот ЖЕ интерфейс будет использоваться лямбдами, мы можем дополнительно пометить его как таковой с помощью @functional Interface :
@FunctionalInterface
public interface Adder {
int add(int a, int b);
}Например, @Override с помощью методов, @Functional Interface объявляет о наших намерениях с помощью Сумматора .
Теперь, независимо от того, используем ли мы @functional Interface или нет, мы все равно можем использовать Сумматор таким же образом:
Adder adder = (a,b) -> a + b; int result = adder.add(4,5);
Но, если мы добавим второй метод в Сумматор, тогда компилятор будет жаловаться:
@FunctionalInterface
public interface Adder {
// compiler complains that the interface is not a SAM
int add(int a, int b);
int div(int a, int b);
}Теперь это было бы скомпилировано без аннотации @functional Interface . Итак, что это нам дает?
Как и @Override , эта аннотация защищает нас от будущих ошибок программиста. Несмотря на то, что законно иметь более одного метода в интерфейсе, это не так, когда этот интерфейс используется в качестве лямбда-цели. Без этой аннотации компилятор сломался бы в десятках мест, где Сумматор использовался в качестве лямбды. Теперь/| он просто врывается Сумматор сам по себе.
3.2. @Native
Начиная с Java 8, в пакете java.lang.annotation появилась новая аннотация под названием Native . Аннотация @Native применима только к полям. Это указывает на то, что аннотированное поле является константой, на которую можно ссылаться из собственного кода . Например, вот как он используется в классе Integer :
public final class Integer {
@Native public static final int MIN_VALUE = 0x80000000;
// omitted
}Эта аннотация также может служить подсказкой для инструментов для создания некоторых вспомогательных заголовочных файлов.
4. Мета-аннотации
Далее, мета-аннотации-это аннотации, которые могут быть применены к другим аннотациям.
Например, эти мета-аннотации используются для настройки аннотаций:
- @Цель
- @Удержание
- @Унаследовано
- @Документировано
- @Повторяемость
4.1. @Target
Объем аннотаций может варьироваться в зависимости от требований. В то время как одна аннотация используется только с методами, другая аннотация может использоваться с конструктором и объявлениями полей.
Чтобы определить целевые элементы пользовательской аннотации, нам нужно пометить ее @Цель аннотация.
@Target может работать с 12 различными типами элементов . Если мы посмотрим на исходный код @SafeVarargs , то увидим, что он должен быть прикреплен только к конструкторам или методам:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface SafeVarargs {
}4.2. @Удержание
Некоторые аннотации предназначены для использования в качестве подсказок для компилятора, в то время как другие используются во время выполнения.
Мы используем аннотацию @Retention , чтобы указать, где в жизненном цикле нашей программы применяется наша аннотация .
Для этого нам нужно настроить @Retention с помощью одной из трех политик хранения:
- Политика удержания.ИСТОЧНИК – не виден ни компилятором, ни средой выполнения
- Политика удержания.КЛАСС – видимый компилятором
- Политика удержания.СРЕДА ВЫПОЛНЕНИЯ – видимая компилятором и средой выполнения
Если нет @Удержание аннотация присутствует в объявлении аннотации, затем политика хранения по умолчанию политика хранения по умолчанию .
Если у нас есть аннотация, которая должна быть доступна во время выполнения:
@Retention(RetentionPolicy.RUNTIME)
@Target(TYPE)
public @interface RetentionAnnotation {
}Затем, если мы добавим некоторые аннотации к классу:
@RetentionAnnotation
@Generated("Available only on source code")
public class AnnotatedClass {
}Теперь мы можем поразмыслить над Аннотированным классом , чтобы увидеть, сколько аннотаций сохраняется:
@Test
public void whenAnnotationRetentionPolicyRuntime_shouldAccess() {
AnnotatedClass anAnnotatedClass = new AnnotatedClass();
Annotation[] annotations = anAnnotatedClass.getClass().getAnnotations();
assertThat(annotations.length, is(1));
}Значение равно 1, потому что @RetentionAnnotation имеет политику хранения RUNTIME , в то время как @Generated | не имеет.
4.3. @Унаследовано
В некоторых ситуациях нам может понадобиться подкласс, чтобы аннотации были привязаны к родительскому классу.
Мы можем использовать @Унаследовано аннотация, чтобы наша аннотация распространялась из аннотированного класса в его подклассы.
Если мы применим @Inherited к нашей пользовательской аннотации, а затем применим ее к Базовому классу :
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface InheritedAnnotation {
}
@InheritedAnnotation
public class BaseClass {
}
public class DerivedClass extends BaseClass {
}Затем, после расширения базового класса, мы должны увидеть, что Производный класс , по-видимому, имеет ту же аннотацию во время выполнения:
@Test
public void whenAnnotationInherited_thenShouldExist() {
DerivedClass derivedClass = new DerivedClass();
InheritedAnnotation annotation = derivedClass.getClass()
.getAnnotation(InheritedAnnotation.class);
assertThat(annotation, instanceOf(InheritedAnnotation.class));
}Без @Унаследовано аннотация, вышеприведенный тест будет провален.
4.4. @Документировано
По умолчанию Java не документирует использование аннотаций в Javadoc.
Но мы можем использовать аннотацию @Documented , чтобы изменить поведение Java по умолчанию .
Если мы создадим пользовательскую аннотацию, которая использует @Documented :
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelCell {
int value();
}И примените его к соответствующему элементу Java:
public class Employee {
@ExcelCell(0)
public String name;
}Затем Employee Javadoc покажет использование аннотаций: