13. Annotation & Reflection Flashcards
Что такое аннотация?
Это языковое средство (механизм), позволяющее встраивать справочную информацию в исходные файлы. Эта информация (мета-дата) называется аннотацией и не меняет порядок выполнения программы и ее код.
Это означает, что аннотация сохраняет неизменной семантику программы. Но эта информация может быть использована различными инструментальными средствами на стадии разработки или развертывания прикладных программ на Java. Например, аннотация может обрабатываться
генераторами исходного кода.
Для чего нужны аннотации:
- Информация для компилятора - аннотации используются компилятором, для обнаружения ошибок
- Во время компиляции - специальные инструменты могут использовать аннотации, например, для генерации кода
- В runtime - аннотации также могут быть доступны во время выполнения программы
Для обозначения этого языкового средства служит также термин метаданные.
Аннотации создаются с помощью механизма, основанного на интерфейсе.
@interface MyAnno {
String str();
int val();
}
Все аннотации состоят только из объявлений методов и эти методы ведут себя аналогично полям.
Объявление аннотации не может включать в себя ключевое слово extends. Но все аннотации автоматически расширяют интерфейс Annotation.
В интерфейсе Annotation переопределяются методы hashCode (), equals () и toString (), определенные в классе Object. В нем также объявляется метод annotationType (), возвращающий объект типа Class, представляющий вызывающую аннотацию.
Аннотацию можно связать с любым объявлением. Например, аннотировать можно классы, методы, поля, параметры и константы перечислимого типа. Аннотированной может быть даже сама аннотация!
Одночленные аннотации - одночленная аннотация состоит из единственного члена. Она действует подобно обычной аннотации, за исключением того, что допускает сокращенную форму указания значения члена. Когда в такой аннотации присутствует только один член, достаточно задать его значение, а когда она применяется, указывать имя ее члена необязательно. Но для применения этой сокращенной формы единственный член аннотации должен иметь имя value.
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface Documentation { String[] authors(); String description(); String since() default "1.7"; //Class theClass(); }
применение - привязка к классу, методу, полю и пр.:
@Documentation(authors = {"Khasmamedov", "Ivanov"}, description = "task for lesson 13", theClass = Object.class) public class Test {
@Documentation( authors = {"Khasmamedov"}, description = "field for lesson 13 (annotations&reflection)", since = "2.0", theClass = Test.class )
int testIntField;
@SuppressWarnings("deprecation") @Documentation( authors = {"Khasmamedov"}, description = "method for lesson 13 (annotations&reflection)", since = "2.0", theClass = A.class )
еще пример: @Target(value = {ElementType.FIELD}) @Retention(value = RetentionPolicy.RUNTIME) public @interface Column { String value(); }
или например из ДЗ (вообще пустая):
@Target(value = {ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NotEmpty {
}
Какие аннотации вы знаете в JDK, для чего они нужны?
9 основных встроенных аннотаций (можно и свои создать):
4- java.lang.annotation: @Retention, @Documented, @Target и @Inherited.
(это к аннотациям)
5 - java.lang.: @Override, @Deprecated, @Functionallnterface, @SafeVarargs @SuppressWarnings
———————————————————————————–
@Retention
Предназначена для применения только в качестве аннотации к другим аннотациям. Она определяет правило удержания (SOURCE, CLASS и RUNTIME)
@Documented
Служит маркерным интерфейсом, сообщающим инструментальному средству разработки, что аннотация должна быть документирована. Она предназначена для применения только в качестве аннотации к объявлению другой аннотации.
@Target
Задает типы элементов, к которым можно применять аннотацию. Она предназначена
для применения только в качестве аннотации к другим аннотациям.
Аннотация @Target принимает один аргумент, который должен быть константой
из перечисления ElementType. Этот аргумент задает типы объявляемых элементов,
к которым можно применять аннотацию.
@Inherited
Это маркерная аннотация, которую можно применять только в другом объявлении аннотации. Более того, она оказывает воздействие только на те аннотации, которые будут применяться в объявлениях классов. Аннотация @Inherited обусловливает наследование аннотации из суперкласса в подклассе.
@Override
Это маркерная аннотация, которую можно применять только в методах. Метод, аннотированный как @Override, должен переопределять метод из суперкласса. Если он этого не сделает, во время компиляции возникнет ошибка. Эта аннотацияслужит для гарантии того, что метод из суперкласса будет действительно переопределен, а не просто перегружен.
@Deprecated
Эта маркерная аннотация обозначает, что объявление устарело и должно быть заменено более новой формой. Начиная с версии JDK 9, аннотация @Deprecated позволяет также указать версию Java, с которой аннотируемый элемент считается не рекомендованным к употреблению и подлежит удалению.
@Functionalinterface
Эта маркерная аннотация предназначена для применения в интерфейсах. Она обозначает, что аннотируемый интерфейс является функциональным, т.е. содержит один и только один абстрактный метод.
@SafeVarargs
Это маркерная аннотация применяется в методах и конструкторах. Она указывает на отсутствие каких-нибудь небезопасных действий, связанных с параметром переменной длины.
@SuppressWarnings
Эта аннотация обозначает, что следует подавить одно или несколько предупреждений, которые могут быть выданы компилятором. Подавляемые предупреждения указываются по имени в строковой форме.
@Target(value = {ElementType.FIELD}) @Retention(value = RetentionPolicy.RUNTIME) public @interface Column { String value(); }
Расскажите про Target и Retention?
@Retention является встроенной аннотацией Java: определяет правило удержания аннотации (в какой момент времени будет удерживаться! или - стадия доступности аннотации):
Правила удержания определяют момент, когда аннотация отбрасывается.
В Java определены три таких правила, инкапсулированные в перечисление
java.lang.annotation.RetentionPolicy.
Это правила SOURCE, CLASS и RUNTIME.
1. Аннотации по правилу удержания SOURCE хранятся только в исходном файле и отбрасываются при компиляции.
2. Аннотации по правилу удержания CLASS сохраняются
в файле с расширением .class во время компиляции. Но они недоступны для виртуальной машины JVM во время выполнения.
3. Аннотации по правилу удержания RUNTIME сохраняются в файле с расширением .class во время компиляции и остаются доступными для виртуальной машины JVM во время выполнения. Это означает, что правило удержания RUNTIME предоставляет аннотации наиболее высокую степень сохраняемости.
Если для аннотации не указано никакого правила удержания, то применяется
правило удержания CLASS.
————————————————————————————-
@Target
Задает типы элементов, к которым можно применять аннотацию. Она предназначена для применения только в качестве аннотации к другим аннотациям.
Аннотация @Target принимает один аргумент, который должен быть константой из перечисления ElementType (можно задать одно или несколько значений этих констант). Этот аргумент задает типы объявляемых элементов, к которым можно применять аннотацию.
ElementType.TYPE, означает что она может быть объявлена перед классом, интерфейсом или enum. Объявление @Target в любых других местах программы будет воспринято компилятором как ошибка.
Остальные возможные типы аннотации @Target:
PACKAGE - назначением является целый пакет (package);
TYPE - класс, интерфейс, enum или другая аннотация:
METHOD - метод класса, но не конструктор (для конструкторов есть отдельный тип CONSTRUCTOR);
PARAMETER - параметр метода;
CONSTRUCTOR - конструктор;
FIELD - поля-свойства класса;
LOCAL_VARIABLE - локальная переменная (обратите внимание, что аннотация не может быть прочитана во время выполнения программы, то есть, данный тип аннотации может использоваться только на уровне компиляции как, например, аннотация @SuppressWarnings);
ANNOTATION_TYPE - другая аннотация.
TYPE_PARAMETER - тип параметра
TYPE_USE - везде, где применяются типы данных (тип, возвращаемый методом, тип объекта по ссылке this в теле метода, приведение типов, уровни доступа к массиву, наследуемый класс, оператор throws, а также обобщенные типы, включая границы параметров и аргументы обобщенного типа)
Еще раз пример:
@Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interface Documentation { String[] authors(); String description(); String shortDescription(); String since() default "1.7"; }
Какие свойства возможны в аннотациях?
Аннотация описывается как обычный интерфейс, но
с ограничениями.
- Все аннотации состоят только из объявлений методов (без аргументов) и эти методы ведут себя аналогично полям.
- методы аннотаций могут иметь значение по умолчанию (default в объявлении члена аннотации.) (((String since() default “1.7”)))
Атрибуты могут иметь только следующие типы:
- примитивы
- String
- Class или «any parameterized invocation of Class»
- enum
- annotation
- одномерный массив элементов любого из вышеперечисленных типов
с
Что такое рефлексия?
Рефлексия - это языковое средство (инструмент) для получения сведений о классе во время выполнения программы.
Рефлексия в Java осуществляется с помощью Java Reflection API. Этот интерфейс API состоит из классов пакетов java.lang и java.lang.reflect
Воспользовавшись рефлексией можно получить объект типа Class. Самый простой способ - вызвать метод getClass (),определенный в классе Object. Этот метод возвращает объект типа Class, который представляет вызывающий объект.
Рефлексия позволяет исследовать информацию о полях,
методах и конструкторах классов. Можно также выполнять операции над
полями и методами, которые исследуются:
Имея в своем распоряжении объект типа Class, можно воспользоваться его методами для получения сведений о различных элементах, объявленных в классе (getMethod (), getField () и getConstructor ()). Из объекта типа Class, Method, Field или Constructor можно получить конкретные аннотации, связанные с этим объектом, вызвав метод getAnnotation ().
Для того чтобы получить сразу все аннотации, имеющие аннотацию @Retention
с установленным правилом удержания RUNTIME и связанные с искомым элементом, достаточно вызвать метод getAnnotations () для этого элемента. Метод getAnnotations () возвращает массив аннотаций.
for (Field field : cl.getDeclaredFields()) {
Annotation[] annotations = field.getAnnotations();
Этот метод может быть вызван для объектов типа Class, Method, Constructor и Field.
Методы getAnnotation () и getAnnotations () определены в интерфейсе AnnotatedElement, который входит в состав пакета java.lang.reflect. Этот интерфейс поддерживает рефлексию для аннотации и реализуется в классах Method, Field, Constructor, Class и Package. Кроме методов getAnnotation () и getAnnotations (), в интерфейсе AnnotatedElement определяются два других метода. Первый метод, getDeclaredAnnotations(), имеет следующую общую форму: Annotation[] getDeclaredAnnotations()
Данный метод возвращает ненаследуемые аннотации, присутствующие в вызывающем объекте. А второй метод, isAnnotationPresent (),имеет такую общую форму: boolean isAnnotationPresent(Class extends Annotation>
тип_ аннотации)
Этот метод возвращает логическое значение true, если аннотация, заданная в виде аргумента тип_ аннотации, связана с вызывающим объектом. В противном
случае возвращается логическое значение false. В версии JDK 8 эти методы дополнены методами getDeclaredAnnotation (), getAnnotationsByType ()
и getDeclaredAnnotationsByType ()
Class cl = obj.getClass();
for (Field field : cl.getDeclaredFields()) { Annotation[] annotations = field.getAnnotations(); for (Annotation annotation : annotations) { if (annotation.annotationType() == NotNull.class) { NotNull notNull = field.getAnnotation(NotNull.class); if (notNull != null) { setAccess(field); if (field.get(obj) != null) { result = true; } } } else if (annotation.annotationType() == Regexp.class) { ... }
Что можно сделать с помощью рефлексии и как? Описать общими словами
Для реализации какого-то инструмента, который на основе рефлексии и, допустим аннотаций, будет решать определённую задачу
Class - методы
isAnnotation() - является ли аннотацией
isInterface() - является ли интерфейсом
isArray() - является ли массивом
isPrimitive() - является ли оберткой примитивов
isEnum() - является ли перечислением
getConstructors() - вернёт массив конструкторов (java.lang.reflect.Constructor)
getDeclaredFields() - вернет массив всех полей (java.lang.reflect.Field)
getMethods() - вернет массив всех методов (java.lang.reflect.Method)
Declared or not declared У класс есть пары методов: aClass.getConstructors(); aClass.getDeclaredConstructors(); aClass.getFields(); aClass.getDeclaredFields(); aClass.getMethods(); aClass.getDeclaredFields();
Declared or not declared У класс есть пары методов: aClass.getConstructors(); aClass.getDeclaredConstructors(); aClass.getFields(); aClass.getDeclaredFields(); aClass.getMethods(); aClass.getDeclaredFields();
Создание экземпляра класса без new NewObject no = new NewObject(); Class с = no.getClass(); try { // получаем конструктор с двумя параметрами Constructor constructor = с.getDeclaredConstructor(String.class, String.class); NewObject newObject = constructor.newInstance("Mikhail", "Maksaimer"); System.out.println(newObject); } catch (NoSuchMethodException e) { // если такого конструктора не существует e.printStackTrace(); } Рузальтат: NewObject{firstName='Mikhail', secondName='Maksaimer'}
Можно создать объект без слова new, а также можно вызывать методы без их
упоминания в коде!!!
Вызов метода try { NewObject newObject = new NewObject("Mikhail", "Maksaimer"); Class c = newObject.getClass(); Method toString = c.getMethod("toString"); System.out.println((String) toString.invoke(newObject)); } catch (NoSuchMethodException e) { // если такого конструктора не существует e.printStackTrace(); } Резульат: NewObject{firstName='Mikhail', secondName='Maksaimer'}