Java. Полное руководство. 8-е издание. С. Н. Тригуб Перевод с английского и редакция
Скачать 25.04 Mb.
|
public static void m y M e t h O { // ... 3 0 Часть I. Язык Эта аннотация связана с методом myMeth () . Посмотрите внимательно на ее синтаксис. За именем аннотации, которому предшествует @, следует взятый в скобки список инициализаторов членов. Чтобы присвоить значение члену, оно присваивается его имени. Таким образом, в этом примере строка " Пример аннотации " присваивается члену str объекта аннотации My Anno. Обратите внимание на то, что в этом присваивании никаких скобок за s t г не следует. Когда члену аннотации присваивается значение, используется только его имя. То есть члены аннотации в данном контексте выглядят как поля. Политика удержания аннотации Прежде чем объяснять аннотации дальше, необходимо обсудить политику удержания аннотаций (annotation retention policies). Политика удержания определяет, в какой точке аннотация отбрасывается. Java определяет три такие политики, которые инкапсулированы в перечислении j ava. lang. annotation. RetentionPolicy. Это SOURCE, CLASS и Аннотации с политикой удержания SOURCE содержатся только в исходном файле и отбрасываются при компиляции. Аннотации с политикой удержания CLASS сохраняются в файле . class вовремя компиляции. Однако они недоступны JVM вовремя выполнения. Аннотации с политикой удержания RUNTIME сохраняются в файле .class вовремя компиляции и остаются доступными JVM вовремя выполнения. То есть политика RUNTIME предоставляет аннотации наиболее высокую степень постоян ства. На заметку Аннотация объявлений локальных переменных в файле class не сохраняется. Политика удержания для аннотации задается с помощью одной из встроенных аннотаций Java: ©Retention. Ее общая форма показана ниже политика удерж ания) Здесь полит икану,держ ания должна быть одной из описанных ранее констант. Если для аннотации не указано никакой политики удержания, используется политика удержания В следующем примере аннотации My Anno устанавливается политика удержания RUNTIME с помощью аннотации @Ret ent ion. То есть аннотация My Anno будет доступна JVM вовремя выполнения программы (RetentionPolicy.RUNTIME) ©interface MyAnno { String s t r (); int v a l (Получение аннотаций вовремя выполнения с использованием рефлексии Хотя аннотации спроектированы, в основном, для использования инструментами разработки и развертывания, если они задают политику удержания RUNTIME, то могут быть опрошены вовремя выполнения любой программой Java за счет использования рефлексии. Рефлексия — это средство, позволяющее получить информацию о классе вовремя выполнения программы. Программный интерфейс (API) рефлексии содержится в пакете j ava. lang .reflect. Существует множество спо Глава 12. Перечисления, автоупаковка и аннотации (метаданные) 3 0 7 собов применения рефлексии, и мы не будем здесь обсуждать их все. Тем не менее мы пройдемся по нескольким примерам, имеющим отношение к аннотациям. Первый шаг в использовании рефлексии — это получение объекта класса Class, представляющего класс, аннотацию которого нужно получить. Класс Class — это один из встроенных классов Java, определенный в пакете j ava. lang. Он детально рассматривается в части II. Есть разные способы получения объекта класса Class. Один из простейших — вызвать метод getClasst), определенный в классе Obj ect. Его общая форма показана ниже Class> Метод возвращает объект класса Class, который представляет вызывающий объект. На заметку Обратите внимание на символы >, следующие за словом class в объявлении метода getClass () выше. Это связано со средствами обобщений Java. Метод get Class () и несколько других связанных с рефлексией методов, обсуждаемых в этой главе используют обобщения. Обобщения описаны в главе 14. Однако понимание обобщений необязательно, чтобы уяснить фундаментальные принципы рефлексии. После того как получите объект класса Class, вы можете использовать его методы для получения информации о различных элементах, объявленных в классе, включая его аннотацию. Если хотите получить аннотации, ассоциированные с определенным элементом класса, следует сначала получить объект, представляющий этот элемент. Например, класс Class представляет (помимо прочих) методы getMethod () , getField ( ) и getConstructor ( ), которые возвращают информацию о методе, поле и конструкторе соответственно. Эти методы возвращают объекты классов Method, Field и Чтобы понять этот процесс, рассмотрим пример, который получает аннотации, ассоциированные с методом. Для этого вы сначала получаете объект класса Class, представляющий класс, затем вызываете метод getMethod () этого объекта, указав имя метода. Метод getMethod () имеет следующую общую форму getMethod(String имяМетода, Class> . типыПараметров) Имя метода передается в параметре имяМетода. Если метод принимает аргументы, то объекты класса C l a s s , представляющие их типы, также должны быть указаны в списке типыПараметров. Обратите внимание на то, что типыПараметров — это список аргументов переменной длины (vararg). Это означает, что вы можете задать столько типов параметров, сколько нужно, включая нуль. Метод get Method () возвращает объект класса Method, который представляет метод. Если метод не может быть найден, передается исключение От объектов классов Class, Method, Field или Constructor вы можете получить специфические аннотации, ассоциированные с этим объектом, обратившись к методу getAnnotat ion () . Его общая форма представлена ниже. <А extends Annotation> getAnnotation(Class типАннотации) Здесь типАннотации— это объект класса C l a s s , представляющий аннотацию, в которой вы заинтересованы. Метод возвращает ссылку на аннотацию. Используя эту ссылку, вы можете получить значения, ассоциированные с членами аннотации. Метод возвращает значение nul 1 , если аннотация не найдена это случай, когда аннотация не имеет аннотации ©Retention, устанавливающей политику удержания RUNTIME. Ниже показана программа, которая собирает все описанные части вместе и использует рефлексию для отображения аннотации, ассоциированной с методом jav a .lang.annotation.*; import java.lang.reflect.*; 3 0 Часть I. Язык Java // Объявление типа аннотации MyAnno { String s t r (); int v a l (); } class Meta { // Аннотировать метод = "Пример аннотации, val = 100) public static void myMeth() { Meta ob = new M e t a O ; // Получить аннотацию из метода // и отобразить значения членов try { // Для начала получить Class, // представляющий класс с = ob.getClass(); // Теперь получить объект Method, // представляющий этот метод с .getMethod("myMeth"); // Далее получить аннотацию класса anno = m.getAnnotation(MyAnno.class); // Наконец, отобразить аннотацию t r () + " " + anno.val()); } catch (NoSuchMethodException exc) { System.out.p r Метод не найден static void main(String a r g s []) { m y M e t h (Вот как выглядит результат работы этой программы. Пример аннотации Эта программа использует рефлексию, как описано, чтобы получить и отобразить значения переменных s t r и v a l аннотации My Anno, ассоциированной с методом myMeth () в классе Meta. Есть несколько моментов, на которые следует обратить особое внимание. Первый момент — выражение My A n n o . c l a s s в строке anno = Это выражение вычисляется как объект C l a s s типа My Anno — аннотация. Это называется литералом класса Вы можете использовать выражения этого типа всякий раз, когда требуется объект C l a s s известного класса. Например, следующий оператор служит для получения объекта C l a s s для класса Meta. Class> с = Конечно, такой подход работает, только когда вызнаете имя класса объекта заранее, что не всегда возможно. Вообще, вы можете получать литерал класса для классов, интерфейсов, элементарных типов и массивов. (Помните, что синтаксис < ?> имеет отношение к средствам обобщений Java; они описаны в главе 14.) Глава 12. Перечисления, автоупаковка и аннотации (метаданные) 3 0 Второй интересный момент — это способ получения значений, ассоциированных с переменными str и val, когда они выводятся в следующей строке t r () + " " + Обратите внимание на то, что они вызываются с применением синтаксиса вызова методов. Тот же подход используется всякий раз, когда требуется получить член аннотации. Второй пример применения рефлексии В предыдущем примере метод myMeth () не имел параметров. Другими словами, когда вызывался метод getMethod () , передавалось только имя myMeth. Однако для того, чтобы получить метод, который имеет параметры, следует задать объекты класса, представляющие типы этих параметров, в виде аргументов метода getMethod () . Например, ниже показана слегка измененная версия предыдущей программы java.lang.annotation.*; import java.lang.reflect.* ; ©Retention(RetentionPolicy.RUNTIME) ©interface MyAnno { String s t r (); int v a l (); } class Meta { // myMeth теперь имеет два аргумента = "Два параметра, val = 19) public static void m y M e t h (String str, int 1 ) { Meta ob = new M e t a O ; try { Class> с = ob.getClass(); // Здесь указываются типы параметров m = с .getMethod("myMeth", String.class, int.class); MyAnno anno = m.getAnnotation(MyAnno.class); System.out.println(anno.s t r () + " " + anno.val()); } catch (NoSuchMethodException exc) Метод не найден static void main(String a r g s []) { m y M e t h (тест Результат работы этой версии будет таким. Два параметра В этой версии метод myMeth () принимает параметры типа String и int. Чтобы получить информацию об этом методе, метод getMethod () должен быть вызван следующим образом 3 1 Часть I. Язык Java Method ш = с .getMethod("myMeth", String.class, Здесь объекты Class, представляющие типы String и int, передаются в виде дополнительных аргументов. Получение всех аннотаций Вызвав метод getAnnotations () с позицией, вы можете получить сразу все аннотации, имеющие аннотацию ©Retention, с установленной политикой удержания RUNTIME, которые ассоциированы с этой позицией. Общая форма этого метода выглядит так Этот метод возвращает массив аннотаций. Метод getAnnotations () может быть вызван для объектов классов Class, Method, Constructor и Вот еще один пример с рефлексией, который показывает, как получить все аннотации, ассоциированные с классом и методом. Он объявляет две аннотации. Затем он использует их для аннотирования класса и метода Показать все аннотации для класса и метода import java.lang.annotation.*; import java.lang.reflect.*; ©Retention(RetentionPolicy.RUNTIME) ©interface MyAnno { String s t r (); int v a l (); } ©Retention(RetentionPolicy.RUNTIME) ©interface What { String description(); } ©What(description = "Аннотация тестового класса = "Meta2"/ val = 99) class Meta2 { ©What(description = "Аннотация тестового метода = "Testing", val = 100) public static void m y M e t h () { Meta2 ob = new M e t a 2 (); try { Annotation a n n o s [] = o b .getClass().getAnnotations(); // Отобразить все аннотации для M e t a 2 Все аннотации для M e t a 2 :"); for(Annotation a : annos) System.out.println(a); System.out.println(); // Отобразить все аннотации для myMeth. Method m = ob.getClass( ).getMethod("myMeth"); annos = Все аннотации для myMeth:"); for(Annotation a : annos) Глава 12. Перечисления, автоупаковка и аннотации (метаданные) 3 1 а catch (NoSuchMethodException ехс) Метод не найден static void main(String a r g s []) { m y M e t h (Ниже показан результат работы этой программы. Вс.е аннотации для Meta2 : @What(descript1оп=Аннотация тестового класса Все аннотации для myMeth: @What(descript1оп=Аннотация тестового метода Эта программа использует метод getAnnotat ions () для получения массива всех аннотаций, ассоциированных с классом Meta2 и методом myMeth () . Как объяснялось, метод getAnnotat ions () возвращает массив объектов интерфейса Annotation. Вспомните, что Annotation — это суперинтерфейс для всех интерфейсов аннотаций и что он переопределяет метод toString () класса Object. То есть когда выводится ссылка на интерфейс Annotation, вызывается его метод toString () для создания строки, описывающей аннотацию, что и демонстрирует предыдущий пример. Интерфейс A n n o ta te d E le m e n Методы getAnnotat ion () и getAnnotat ions ( ), использованные в предыдущем примере, определены интерфейсом Annot at edEl ement , который определен в пакете j ava. lang .reflect. Этот интерфейс поддерживает рефлексию для аннотации и реализован классами Method, Field, Constructor, Class и В дополнение к методами, интерфейс AnnotatedElement определяет два других метода. Первый из них — метод get- DeclaredAnnotations () , который имеет следующую общую форму Этот метод возвращает неунаследованные аннотации, представленные в вызывающем объекте. Второй — это метод isAnnotationPresent () , имеющий такую форму isAnnotationPresent(Class extends Annotation> типАннотации) Он возвращает значение true, если аннотация, заданная в типАннотации, ассоциирована с вызывающим объектом. В противном случае метод возвращает значение Использование значений по умолчанию Вы можете придать Членам аннотации значения по умолчанию, которые будут использоваться, когда применяется аннотация. Значение по умолчанию указывается добавлением ключевого словак объявлению члена. Это выглядит следующим образом. тип m e m b e r () default значение 3 1 2 Часть I. Язык Здесь значение должно иметь тип, совместимый с тип Вот как выглядит аннотация @МуАппо, переписанная с использованием значений по умолчанию Объявление типа аннотации, включающее значения по умолчанию MyAnno { String str () default "Тестирование v a l () default Это объявление определяет значение по умолчанию Тестирование" для переменной-члена s t r и 9000 — для переменной-члена v a l . Это означает, что ни одно из значений необязательно указывать при использовании аннотации @МуАппо. Однако любому из них или обоим сразу можно присвоить значение при необходимости. Таким образом, существует четыре способа применения аннотации @МуАппо. ©MyAnno() // значения str и val принимаются по умолчанию ©MyAnno(str = "некоторая строка) // val — по умолчанию ©MyAnno(val = 1 0 0 ) / / s t r — по умолчанию = "Тестирование, val = 100) // нет умолчаний В следующей программе демонстрируется использование значений по умолчанию в аннотациях jav a .lang.annotation.*; import java.lang.reflect.*; // Объявление типа аннотаций с включением значений по умолчанию MyAnno { String str() default "Тестирование int v a l () default 9000; } class Meta3 { // Аннотирование метода с использованием значений по умолчанию static void m y M e t h () { Meta3 ob = new M e t a 3 (); // Получить аннотацию к методу // и отобразить значения ее членов try { Class> с = o b .getClass(); Method m = с .getMethod("myMeth"); MyAnno anno = m.getAnnotation(MyAnno.class); System.out.println(anno.s t r () + " " + anno.val()); } catch (NoSuchMethodException exc) Метод не найден static void main(String a r g s []) { m y M e t h (Вывод будет таким. Тестирование 900 0 } Глава 12. Перечисления, автоупаковка и аннотации (метаданные) 3 1 3 Аннотация-маркер Это — специальный вид аннотаций, которые не содержат членов. Единственное назначение аннотации маркера пометить (маркировать) объявление. То есть его присутствие как аннотации существенно. Лучший способ определить, имеется ли аннотациям аркер, — воспользоваться методом isAnnotationPresent () , который определен винтер фей се Рассмотрим пример использования аннотации-м аркера. Поскольку такая аннотация не имеет членов, следует просто определить, присутствует она или нет j a v a .l a n g .a n n o t a t i o n .*; import j a v a . l a n g . r e f l e c t .*; // Аннотациям арке р . © R e t e n t i o n ( R e t e n t i o n P o l i c y .RUNTIME) © interface M yMarker { } class Marker { // Аннотированием е тода с помощью маркера Обратите внимание на необходимость скобок (). ©MyMarker p ublic static v o i d m y M e t h () { Marker ob = new M a r k e r (); try { M e t h o d m = o b .g e t C l a s s ().g e t M e t h o d (" m y M e t h " ); // Определение наличия аннотации f ( m . i s A n n o t a t i o n P r e s e n t ( M y M a r k e r . c l a s s )) S y s t e m . o u t . p r i n t l n ("MyMarker присутствует m . o u t . p r i n t l n (Метод не найден static v o i d ma i n ( S t r i n g a r g s []) { m y M e t h Показанный ниже вывод подтверждает наличие аннотации присутствует Обратите внимание на то, что нет необходимости после аннотации @МуМагкег указывать скобки. То есть аннотация применяется просто си спользо ванием ее имени. ©MyMarker Н е будет ошибкой указать пустые скобки, однако в этом нет необходимости. Одночленные аннотации О дн очлен н ая аннотация содержит, как должно быть понятно, только один член. Она работает подобно обычной аннотации, за исключением того, что допускает сокращенную форму указания значения члена. Когда присутствует только один член, вы можете просто задать его значение когда аннотация применяется, вы не обязаны указывать имя члена. Однако для того, чтобы использовать это сокращение, член должен иметь имя value. |