Введение в аннотации

J2SE содержит множество нововведений в самом языке Java, в том числе поддержка параметризуемых классов (generics) и улучшенного цикла for (enhanced for loop). В предыдущих статьях мы подробно рассказывали о параметризуемых классах и об улученном цикле for, а в этой статье мы рассмотрим аннотации, встроенные в J2SE 5.0.

Что же такое аннотации? Аннотации - они же JSR-175 (Meta-data) - позволяют связывать метаданные с программными элементами (такими как классы, интерфейсы и методы). Они могут служить дополнительными модификаторами без изменения основного кода для своих элементов.

Само введение метаданных в исходный код не является новым для J2SE 5.0. Вы можете добавить тег @deprecated в комментариях к методу в javadoc, и компилятор будет обрабатывать его как метаданные метода. Такая возможность появилась с выходом версии J2SE 1.0. Уже начальный релиз платформы был ориентирован на сокращение использования метода getenv() (хотя это было реализовано только в версии 1.1). Суть осталась той же, за исключением символа @ в синтаксисе. Изменилось только место -- тег аннотации перешел в код, а не в комментарий. Преимущество в том, что аннотации являются способом поддержки программной модели объявлений.

Итак, первая аннотация, которая идет с J2SE 5.0: @Deprecated. Обратите внимание, что здесь заглавная буква D. @Deprecated функционально работает в коде также как и @deprecated в javadoc, связанный с классом или методом. С помощью флагов и тега @Deprecated вы сообщаете компьютеру предупредить пользователя, что используется тот или иной класс или метод.

Класс Main содержит метод deprecatedMethod(), у которого флагом является аннотация @Deprecated, а комментарием @deprecated:

public class Main {

 
/**
   *
@deprecated устарел. Используйте вместо него Use System.foobar().
   */

 
@Deprecated
 
public static void deprecatedMethod() {
   
System.out.println("Don't call me");
 
}
}

Компилируйте класс с аннотацией так же, как без нее:

> javac Main.java

Как и следовало ожидать, получился класс Main.class.

Если вы используете метод deprecated, то получите предупреждение при компиляции - такое же, как при использовании тега @deprecated в javadoc. Пример:

public class User {
 
public static void main(String args[]) {
   
Main.deprecatedMethod();
 
}
}

Скомпилируется:

> javac User.java

и вы увидите следующее предупреждение об использовании класса deprecated:

Note: User.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation
for details.

User.java использует или заменяет deprecated API. Перекомпилируйте с -Xlint:deprecation, чтобы посмотреть детали. Добавление -Xlint в строку компиляции покажет, что именно не так:

> javac -Xlint:deprecation User.java

   User.java:
3: warning: [deprecation] deprecatedMethod() in
   Main has been deprecated
          Main.deprecatedMethod
();
              ^
  
1 warning

Замена комментария @deprecated на аннотацию @Deprecated не принесет ничего существенно нового в систему. Это слега изменяет способ, но делает все то же самое. А вот следующие две аннотации @Override и @SuppressWarnings действительно меняют функциональность платформы.

Аннотация @Override может использоваться вместе с объявлением метода. После того как вы ввели имя, можете добавить аннотацию @Override для установки значение флага функции, чтобы переписать метод суперкласса. Зачем так делать? Чтобы найти ошибку раньше. Сколько раз вы собирались переписать метод, но то в названии метода была ошибка, то определены неправильные аргументы, а то неверный тип возвращаемого значения? Иными словами, сколько раз вам приходилось создавать новый метод вместо того, чтобы переписать уже существующий? Используя @Override, вы найдете ошибку в текущем классе намного раньше:

public class Overrode {
 
@Override
 
public int hashcode() {
   
return 0;
 
}

 
@Override
 
public boolean equals(Object o) {
   
return true;
 
}
}

Здесь ошибка в том, что название метода должно быть hashCode, а не hashcode. Предполагается, что объявление метода находится где-то в описании класса. Если не использовать первую аннотацию @Override, сколько у вас займет времени, чтобы обнаружить, что ваш метод hashCode (с заглваной буквой) не вызывается, и вы получаете некоторый результат по умолчанию от родительского класса Object. Благодаря аннотации @Override, компиляция класса выдаст ошибку, сообщающую вам о проблеме (метод не переписывается из своего суперкласса):

> javac Overrode.java

   Overrode.java:
2: method does not override a method from its
   superclass
       
@Override
        
^
  
1 error

Чем скорее вы найдете ошибку такого происхождения, время на ее исправление сильно сокращается. Обратите внимание, что метод hashCode никогда не возвращает константу. Более подробное описание использования hashCode и equals() вы найдете в 8 пункте в книге Джошуа Блош "Путеводитель по эффективному программированию на Java".

Последняя из трех новых аннотаций в J2SE 5.0 - @SuppressWarnings - самая интересная. Она сообщает компилятору не предупреждать вас, о чем он обычно предупреждает. Предупреждения относятся к разным категориям, и вы можете сообщить аннотации, какие категории вас интересуют. Компилятор javac определяет семь свойств для вывода предупреждений: все, deprecation, непроверенные, проход при невыполнении условия, неверные пути, последовательные и конечные. (В спецификации языка определены только два из этих типов: deprecation и непроверенные.)

В целях демонстрации, посмотрим на блокировку опции "проход при невыполненных условиях". Начнем со следующего класса. В этом классе пропущено break в каждом случае switch:

public class Fall {
 
public static void main(String args[]) {
   
int i = args.length;
   
switch (i) {
   
case 0:
      System.out.println
("0");
   
case 1:
      System.out.println
("1");
   
case 2:
      System.out.println
("2");
   
case 3:
      System.out.println
("3");
   
default:
      System.out.println
("Default");

   
}
  }
}

Скомпилируйте класс с помощью javac. Вы увидите, что просто создается файл .class без всяких предупреждений:

javac Fall.java

Если вы хотите, чтобы компилятор предупреждал вас о невыполнении условий switch (т.е. что пропущено одно или более утверждений break), скомпилируйте с опцией Xlint:fallthrough :

javac -Xlint:fallthrough Fall.java

Вы увидите следующие предупреждения:

Fall.java:6: warning: [fallthrough] possible fall-through 
   into case
            case 1: System.out.println("1");
            ^
   Fall.java:7: warning: [fallthrough] possible fall-through 
   into case
            case 2: System.out.println("2");
            ^
   Fall.java:8: warning: [fallthrough] possible fall-through 
   into case
            case 3: System.out.println("3");
            ^
   Fall.java:9: warning: [fallthrough] possible fall-through 
   into case
            default : System.out.println("Default");
            ^
   4 warnings

Что делать, если вы не придаете значения тому, что в каждом случае switch пропускается break? Вот где вам пригодится аннотация @SuppressWarnings. Вставьте эту строчку перед объявлением метода main():

@SuppressWarnings("fallthrough")

Скомпилируйте с опцией -Xlint:fallthrough:

javac -Xlint:fallthrough Fall.java

и получите файл .class без единого предупреждения.

Аннотация @SuppressWarnings может использоваться для подавления других предупреждений, например, когда вы используете коллекции, не указывая типа данных элементов коллекции. Не стоит использовать аннотацию @SuppressWarnings, чтобы просто избежать предупреждений компилятора. Применяйте это там, где нельзя избежать предупреждений, например, при использовании библиотеки, которая не была создана вместе с параметризуемыми классами (generics).

Это что касается встраиваемых аннотаций. Маленькое дополнение: аннотации (со своими аргументами) обычно определяются отдельной строкой сами по себе.

Определяя свои собственные аннотации, вы можете сделать намного больше, чем просто использовать аннотации, введенные в J2SE 5.0. Здесь вы узнаете, как создавать свои аннотации.

Теги: annotations java 5