Введение в аннотации
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. Здесь вы узнаете, как создавать свои аннотации.