Как работает аннотация org.jetbrains.annotations.Contract
?
Как поддерживает IntelliJ IDEA?
JetBrains @Контрактная аннотация
Ответ 1
Прежде всего, я должен сказать, что эта аннотация предназначена только для IDEA для проверки возможных ошибок. Компилятор Java будет игнорировать его почти полностью (он будет в скомпилированном артефакте, но не имеет никакого эффекта). Сказав это...
Цель аннотации состоит в том, чтобы описать контракт, который будет подчинен методу, что поможет IDEA уловить проблемы в методах, которые могут вызвать этот метод. Контракт, о котором идет речь, представляет собой набор разделяемых разделов с запятой, каждый из которых описывает ввод и вывод, который гарантированно произойдет. Причина и эффект разделяются ->
и описывают случай, когда при предоставлении X методу Y всегда будет иметь значение. Вход описывается как список, разделенный запятыми, описывающий случай с несколькими входами.
Возможные входы: _
(любое значение), null
, !null
(not-null), false
и true
, а возможные выходы добавляют fail
к этому списку.
Так, например, null -> false
означает, что при условии ввода null
в качестве результата будет 24 > boolean. null -> false; !null -> true
расширяется на этом, чтобы сказать, что null
всегда будет возвращать false
, а значение не null
всегда будет возвращать true и т.д. Наконец, null -> fail
означает, что метод будет генерировать исключение, если вы передадите ему нулевое значение.
Для примера с несколькими аргументами null, !null -> fail
означает, что в методе с двумя аргументами, если первый аргумент равен null, а второй - не null, исключение будет выбрано, гарантировано.
Если метод не изменяет состояние объекта, а просто возвращает новое значение, тогда вы должны установить pure
в значение true.
Ответ 2
Официальная документация описывает формальную грамматику поддерживаемые и распознанные значения для аннотации.
В условиях неспециалиста:
- Контракт может содержать 1 или более связанных с ним статей.
- Предложение всегда
[args] -> [effect]
- Args - это 1 или более ограничений, которые определены как
any | null | !null | false | true
- Эффекты - это только одно ограничение или
fail
Давайте рассмотрим быстрый пример - один из моих фаворитов: "Независимо от того, что вы переходите в этот метод, это вызовет исключение".
@Contract("_-> fail")
public void alwaysBreak(Object o) {
throw new IllegalArgumentException();
}
Здесь мы используем _
или "any", чтобы указать, что независимо от того, что мы передаем в этот метод, мы собираемся исключить исключение.
Что, если мы солгали и сказали, что этот метод вернет true
безоговорочно?
@Contract("_-> true")
public void alwaysBreak(Object o) {
throw new IllegalArgumentException();
}
IntelliJ вызывает некоторые предупреждения об этом.
Он также (очевидно) расстроен, что мы сказали, что возвращаем логическое значение, когда мы находимся в методе void...
Основные моменты, когда вы захотите использовать @Contract
, - это когда:
- Вы хотите гарантировать, что вы вернете true или false
- Вы хотите гарантировать, что вы возвращаете ненулевое значение заданных ограничений
- Вы хотите четко указать, что вы можете вернуть нулевое значение заданных ограничений
- Вы хотите четко указать, что вы выбрали исключение, заданное ограничение
Чтобы не сказать, что @Contract
совершенен; отнюдь не. Он не может делать очень глубокий анализ в определенных контекстах, но наличие этого в вашей базе кода позволяет вашей инструментарию делать этот вид анализа бесплатно.
Ответ 3
Как работает аннотация
org.jetbrains.annotations.Contract
?
Хотя предыдущие ответы являются информативными, я не чувствую, что они обращаются к оперативному слову "работа" в вашем вопросе. То есть они не объясняют, что IntelliJ делает за кулисами, чтобы реализовать свои аннотации, чтобы вы могли легко создавать свои собственные с нуля.
Если вы посмотрите на исходный код ниже, вы можете подумать, что это кажется слишком сложным (или, по крайней мере, многословным) для чего-то столь же простого звучания, как @NotNull
. Я согласен с вами, и это одна из причин, по которой я обычно избегаю @Contract
-подобных предложений, которые не являются "простыми и простыми" (например, @NotNull
), а вместо этого JavaDoc my prereqs напрямую.
Я вообще не рекомендую использовать сложные аннотации контрактов, несмотря на ненависть, которую я мог бы получить за пропуски этого модного нового поезда, и вот некоторые причины, по которым:
- Аннотации могут быть сложными -e.g. с несколькими вложенными аннотациями в своих собственных определениях и/или "грамматикой" с появлением полноты Тьюринга. Это может привести к ложному чувству уверенности во время компиляции, маскируя истинного виновника ошибки за слоями неясности/абстракции и не создавая изначально намеченных предупреждений.
- Подобно, но отличается от моего предыдущего момента, аннотации часто скрывают сложную логику от разработчика в нескольких ключевых словах, создавая трудно понятный код для людей и/или необычное поведение, которое может быть сложно отлаживать.
- Конфигурация приложения часто увидела маскарад в качестве аннотаций. Взгляните на Spring framework.
- Синтаксис для определения контрактов по-и-большому (IMHO) довольно уродлив и Makefile-ish.. Например, взгляните на некоторые определения аннотаций JetBrains и поддерживающие файлы разбросанный по его репо. Обратите внимание на многочисленные XML файлы и обширную саморегуляцию? Я бы вряд ли назвал это забавой для написания и поддержки, особенно учитывая постоянно меняющийся характер аннотаций, возглавляемых back-and-forth между Android и более крупным сообществом Java.
Некоторые вопросы для рассмотрения:
- Заходит ли он слишком далеко, когда исходный код аннотации подходит к сложности самого источника, который он комментирует?
- Является ли код ниже на самом деле намного лучше, чем просто проверять значение null во время выполнения и регистрировать исключения с помощью stacktraces? Включение чего-то подобного заставляет ваших пользователей читать, понимать и, возможно, исправлять ошибки в другом наборе зависимостей, которые определяют ваши аннотации.
Заимствованный из еще один длинный пост о семантике контракта, о котором я бы сказал, только далее служит моей точке:
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.annotation.Nonnull;
import javax.annotation.meta.TypeQualifierDefault;
/**
* This annotation can be applied to a package, class or method to indicate that the class fields,
* method return types and parameters in that element are not null by default unless there is: <ul>
* <li>An explicit nullness annotation <li>The method overrides a method in a superclass (in which
* case the annotation of the corresponding parameter in the superclass applies) <li> there is a
* default parameter annotation applied to a more tightly nested element. </ul>
* <p/>
* @see https://stackoverflow.com/a/9256595/14731
*/
@Documented
@Nonnull
@TypeQualifierDefault(
{
ElementType.ANNOTATION_TYPE,
ElementType.CONSTRUCTOR,
ElementType.FIELD,
ElementType.LOCAL_VARIABLE,
ElementType.METHOD,
ElementType.PACKAGE,
ElementType.PARAMETER,
ElementType.TYPE
})
@Retention(RetentionPolicy.RUNTIME)
public @interface NotNullByDefault
{
}
Когда и какие контракты вы должны использовать?
Я предлагаю придерживаться того, чьи намерения кристально чисты от их имени, и избегать тех, у кого есть свой набор семантики и языковых определений.
Примером использования, несмотря на предыдущий сегмент, является @NotNull
, но он ограничивается тем, что все параметры объекта должны быть нулевыми.
Примером сортировки, чтобы избежать, являются такие, как Android и IntelliJ @Contract(...)
. Хотя мне нравятся их IDE, детали их аннотаций довольно сложны и в конечном итоге превратились в источник большего количества проблем и несовместимости платформы для меня, чтобы отслеживать (по общему признанию, из-за моего собственного незнания в создании новых контрактов почти в 100% случаев, но почему так сложно понять это?)
Резюме/Заключение
Аннотации - отличная идея, явно созданная программистами, которые хотят "кодифицировать" свою документацию. Я чувствую, что они зашли слишком далеко в последнее время, превратив документацию в код семантики, который приводит к серьезным головоломкам и неудобным ситуациям. Хуже того, они иногда дают ложное представление о безопасности во время компиляции, не обнаруживая проблем, проявляющихся в их собственных реализациях. Придерживайтесь самых простых и избегайте всего, что похоже на язык, который не является Java (именно это вы и предполагали писать в первую очередь).
Дополнительная литература
Этот краткий список представляет собой сочетание в основном критических источников (w/optimism!) как из StackOverflow, так и из Интернета, которые, как я чувствую, иллюстрируют некоторые мои моменты.
Без особого порядка:
- Аннотации Java - большая ошибка
- Аннотации, аннотации, везде
- Аргументы против аннотаций
- Являются ли аннотации плохими?
- Кошмар аннотации
- Злые аннотации
И после всего этого я понял, что, возможно, все еще не смог полностью решить ваш оригинальный вопрос:)