АБОБА. Справочник по программированию на Java Методическое пособие
Скачать 242.41 Kb.
|
Аннотации (метаданные)Начиная с JDK 5, в Java появилось новое мощное средство, которое позволяет встроить информацию поддержки в исходные файлы. Эта информация, называемая также аннотацией, не меняет действия программы. То есть аннотация сохраняет семантику программ неизменной. Однако эта информация может быть использована различными инструментальными средствами, как во время разработки, так и в период развертывания. Например, аннотация может обрабатываться генераторами исходного кода. Термин метаданные также используется для именования этого средства, но более описательный термин средства аннотирования программ применяется более широко. Основы аннотированияАннотации создаются посредством механизма, основанного на интерфейсе. Начнем с примера. Вот объявление аннотации под названием MyAnno: // Простой тип аннотации. @interface MyAnno { String str(); int val(); } Во-первых, обратите внимание на символ @, предшествующий ключевому слову interface. Это говорит компилятору, что объявлен тип аннотации. Далее отметим два метода-члена – str() и val(). Все аннотации состоят только из объявлений методов. Однако вы не определяете тел этих методов. Вместо этого их реализует Java. Более того, методы ведут себя в большей степени подобно полям, как вы вскоре убедитесь. Аннотация не может включать слова extends. Однако все аннотации автоматически расширяют интерфейс Annotation. То есть Annotation является суперинтерфейсом для всех аннотаций. Он объявлен в пакете java.lang.annotation. В Annotation переопределены методы hashCode(), equals() и toString(), которые определены в Object. В нем также специфицирован метод annotationType(),возвращающий объект Class, который представляет вызывающую аннотацию. Объявив аннотацию, вы можете использовать ее для аннотирования объявления. Любой тип объявления может иметь аннотацию, ассоциированную с ним. Например, аннотироваться могут классы, методы, поля, параметры и константы enum. Аннотированной может быть даже сама аннотация. Во всех случаях аннотация предшествует остальной части объявления. Когда вы применяете аннотацию, то присваиваете значения ее членам. Например, ниже показан вариант применения MyAnno к методу: // Аннотирование метода. @MyAnno(str = "Пример аннотации", val = 100) public static void myMeth() { // ... Эта аннотация связана с методом myMeth(). Посмотрите внимательно на синтаксис аннотации. За именем аннотации, которому предшествует @, следует взятый в скобки список инициализаторов членов. Чтобы присвоить члену значение, значение присваивается имени члена. Таким образом, в этом примере строка “Пример аннотации” присваивается члену str MyAnno. Обратите внимание, что в этом присваивании никаких скобок за str не следует. Когда члену аннотации дается значение, используется только его имя. То есть члены аннотации в данном контексте выглядят как поля. Спецификация политики удержанияПрежде чем объяснять аннотации дальше, необходимо обсудить политики удержания аннотаций (annotation retention policies). Политика удержания определяет, в какой точке аннотация отбрасывается. Java определяет три таких политики, которые инкапсулированы в перечислении java.lang.annotation.RetentionPolicy. Это SOURCE, CLASS и RUNTIME. Аннотации с политикой удержания SOURCE удерживаются только в исходном файле и отбрасываются при компиляции. Аннотации с политикой удержания CLASS сохраняются в файле .class во время компиляции. Однако они недоступны JVM во время выполнения. Аннотации с политикой удержания RUNTIME сохраняются в файле .class во время компиляции и остаются доступными JVM во время выполнения. То есть политика RUNTIME представляет аннотации наиболее высокой степени постоянства. Политика удержания для аннотации задается с помощью одной из встроенных аннотаций Java: @Retention. Ее общая форма показана ниже: @Retention(политика_удержания) Здесь политика_удержания должна быть одной из описанных ранее констант. Если для аннотации не указано никакой политики сохранения, используется CLASS. В следующей версии MyAnno с помощью @Retention указывается политика RUNTIME. То есть MyAnno будет доступна JVM во время выполнения программы. @Retention(RetentionPolicy.RUNTIME) @interface MyAnno { String str(); int val(); } Получение аннотаций во время выполнения с использованием рефлексииХотя аннотации спроектированы в основном для использования инструментами разработки и развертывания, если они специфицируют политику удержания RUNTIME, то могут быть опрошены во время выполнения любой Java-программой за счет использования рефлексии. Рефлексия – это средство, позволяющее получить информацию о классе во время выполнения программы. Программный интерфейс (API) рефлексии содержится в пакете java.lang.reflect. Существует множество способов применения рефлексии, и мы не будем здесь обсуждать их все. Тем не менее, мы пройдемся по нескольким примерам, имеющим отношение к аннотациям. Первый шаг в использовании рефлексии – это получение объекта Class, представляющего класс, чью аннотацию нужно получить. Class – это один из встроенных классов Java, определенный в пакете java.lang. Он детально рассматривается во второй части книги. Есть разные способы получения объекта Class. Один из простейших – вызвать метод getClass(),определенный в Object. Его общая форма показана ниже: final Class getClass() Он возвращает объект Class, который представляет вызывающий объект. После того, как вы получите объект Class, вы должны использовать его методы для получения информации о различных элементах, объявленных в классе, включая его аннотацию. Если вы хотите получить аннотации, ассоциированные с определенным элементом класса, вы должны сначала получить объект, представляющий этот элемент. Например, Class представляет (помимо прочих) методы getMethod(), getField() и getConstructor(), которые получают информацию о методе, поле и конструкторе соответственно. Эти методы возвращают объекты типов Method, Field и Constructor. Чтобы понять этот процесс, рассмотрим пример, который получает аннотации, ассоциированные с методом. Чтобы сделать это, вы сначала получаете объект Class, представляющий класс, затем вызываете метод getMethod() этого объекта, указав имя метода. getMethod() имеет следующую общую форму: Method getMethod(String methName, Class ... paramTypes) Имя метода передается в methName. Если метод принимает аргументы, то объекты Class, представляющие их типы, также должны быть указаны в paramTypes. Обратите внимание, что paramTypes – это список аргументов переменной длины (varargs). Это означает, что вы можете специфицировать столько типов параметров, сколько нужно, включая ноль. getMethod() возвращает объект Method, который представляет метод. Если метод не может быть найден, возбуждается исключение NoSuchMethodException. От объектов Class, Method, Field или Constructor вы можете получить специфические аннотации, ассоциированные с этим объектом, обратившись к getAnnotation(). Его общая форма представлена ниже: Annotation getAnnotation(Class annoType) Здесь annoType – это объект Class, представляющий аннотацию, в которой вы заинтересованы. Метод возвращает ссылку на аннотацию. Используя эту ссылку, вы можете получить значения, ассоциированные с членами аннотации. Метод возвращает null, если аннотация не найдена; это случай, когда аннотация не имеет @Retention, установленную в RUNTIME. Ниже показана программа, которая собирает все описанные части вместе и использует рефлексию для отображения аннотации, ассоциированной с методом. import java.lang.annotation.*; import java.lang.reflect.*; // Объявление типа аннотации. @Retention(RetentionPolicy.RUNTIME) @interface MyAnno { String str(); int val(); } class Meta { // Аннотировать метод. @MyAnno(str = "Пример аннотации", val = 100) public static void myMeth() { Meta ob = new Meta(); // Получить аннотацию из метода // и отобразить значения членов. try { // Для начала получить Class, // представляющий класс. Class c = ob.getClass(); // Теперь получить объект Method, // представляющий этот метод. Method m = c.getMethod("myMeth"); // Далее получить аннотацию класса. MyAnno anno = m.getAnnotation(MyAnno.class); // Наконец, отобразить аннотацию. System.out.println(anno.str() + " " + anno.val()); } catch (NoSuchMethodException exc) { System.out.println("Метод не найден."); } } public static void main(String args[]) { myMeth(); }} Вот как выглядит результат работы этой программы: Пример аннотации 100 Эта программа использует рефлексию, как описано, чтобы получить и отобразить значения str и val в аннотации MyAnno, ассоциированной с myMeth() в классе Meta. Есть две вещи, на которые следует обратить особое внимание. Первая – выражение MyAnno.class в строке: MyAnno anno = m.getAnnotation(MyAnno.class); Это выражение вычисляется как объект Class типа MyAnno – аннотация. Это называется литералом класса. Вы можете использовать выражения этого типа всякий раз, когда требуется объект Class известного класса. Например, следующий оператор служит для получения объекта Class для Meta: Class c = Meta.class; Конечно, такой подход работает, только когда вы знаете имя класса объекта заранее, что не всегда возможно. Вообще вы можете получать литерал класса для классов, интерфейсов, примитивных типов и массивов. Второй интересный момент – это способ, которым значения, ассоциированные с str и val, получаются, когда они выводятся в следующей строке: System.out.println(anno.str() + " " + anno.val()); Обратите внимание, что они вызываются с применением синтаксиса вызова методов. Тот же подход используется всякий раз, когда требуется получить член аннотации. |