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 покажет использование аннотаций: