openjdk.ruOpenJDK на русском

JEP 277: Enhanced Deprecation

Улучшенный механизм пометки API как Deprecated (устаревший)

ОтветственныйStuart Marks
ТипFeature
ОбластьSE
СтатусClosed / Delivered
Выпуск9
Компонентcore-libs / java.lang
Обсуждениеjdk9 dash dev at openjdk dot java dot net
ТрудоёмкостьM
ДлительностьM
РецензентыAlex Buckley, Mark Reinhold
ОдобренBrian Goetz
Создан2014/11/20 23:58
Обновлён2026/07/29 18:58
Задача8065614

Аннотация

Переработать аннотацию @Deprecated и предоставить инструменты для укрепления жизненного цикла API.

Цели

  • Давать в спецификации более полную информацию о статусе API и о том, что с ним планируется сделать.

  • Предоставить инструмент для анализа статического использования устаревших API в приложении.

Что не является целью

Объединение Javadoc-тега @deprecated с аннотацией @Deprecated не является целью этого проекта.

Мотивация

Объявление устаревшим (deprecation) — это способ передать информацию о жизненном цикле API: побудить приложения отказаться от API, удержать их от появления новых зависимостей от этого API и сообщить разработчикам о рисках дальнейшей зависимости от него.

В Java есть два механизма, чтобы объявить API устаревшим: Javadoc-тег @deprecated, появившийся в JDK 1.1, и аннотация @Deprecated, появившаяся в Java SE 5. Спецификация API аннотации @Deprecated, продублированная в The Java Language Specification (спецификация языка Java), гласит:

Элемент программы, помеченный аннотацией @Deprecated, — это элемент, использовать который программистам не рекомендуется, как правило потому, что он опасен или потому, что существует лучшая альтернатива. Компиляторы выдают предупреждение, когда устаревший элемент программы используется или переопределяется в коде, который не объявлен устаревшим.

Однако в итоге аннотацию @Deprecated стали использовать для нескольких разных целей. Из устаревших API на самом деле удалили очень немногие, и поэтому некоторые стали считать, что ничего и никогда удалено не будет. С другой стороны, другие считали, что всё устаревшее со временем может быть удалено, хотя такого намерения тоже никогда не было. (Хотя в спецификациях это прямо не говорилось, в различных документах упоминалось, что устаревшие API в какой-то момент будут удалены.) В результате разработчики получали неясный сигнал о том, что означает @Deprecated и что им следует делать (и следует ли вообще), когда они сталкиваются с использованием устаревшего API. Все путались в том, что на самом деле означает объявление устаревшим, и никто не воспринимал его всерьёз. Это, в свою очередь, сделало удаление чего-либо из API Java SE вообще затруднительным.

Другая проблема объявления устаревшим состоит в том, что предупреждения выдаются только во время компиляции. По мере того как API объявляются устаревшими в очередных версиях Java SE, существующие двоичные файлы продолжают зависеть от устаревших API и использовать их без каких-либо предупреждений. Если бы устаревший API удалили в каком-либо выпуске JDK, даже спустя один или несколько выпусков, в которых он был объявлен устаревшим, это стало бы неприятным сюрпризом для пользователей старых двоичных файлов приложений. Приложение внезапно завершилось бы с ошибкой компоновки, хотя никаких предупреждений ни разу не выдавалось. Хуже того, у разработчиков нет способа проверить, зависят ли существующие двоичные файлы от устаревших API. Это создаёт серьёзное противоречие между возможностью запускать старые двоичные файлы на новых выпусках JDK и необходимостью развивать спецификацию, выводя старые API из употребления.

Подводя итог: механизмы объявления устаревшим применялись в API Java SE непоследовательно, что привело к путанице и в том, что означает объявление устаревшим в принципе, и в том, как правильно его использовать на практике.

Описание

Спецификации

Основная цель улучшения аннотации @Deprecated — дать инструментам более детальную информацию о том, в каком статусе устаревания находится API. Эти инструменты, в свою очередь, используют аннотацию, чтобы сообщать информацию пользователям API. Аннотация @Deprecated сохраняется во время выполнения (runtime retention) и поэтому занимает память в куче. Поэтому информация в ней должна быть минимальной и чётко определённой.

В тип аннотации java.lang.Deprecated добавляются следующие элементы:

  • Метод forRemoval(), возвращающий boolean. Значение true означает, что этот элемент API намечен к удалению в одном из будущих выпусков. Значение false означает, что элемент API объявлен устаревшим, но удалять его в будущем выпуске сейчас не планируется. Значение этого элемента по умолчанию — false.

  • Метод с именем since(), возвращающий String. Эта строка должна содержать выпуск или номер версии, в которой этот API был объявлен устаревшим. Синтаксис строки произвольный, но нумерация выпусков должна следовать той же схеме, что и в Javadoc-теге @since для проекта, содержащего устаревший API. Обратите внимание: это значение не дублирует Javadoc-тег @since, потому что тот фиксирует выпуск, в котором API появился, а метод since() в аннотации @Deprecated фиксирует выпуск, в котором API был объявлен устаревшим. Значение этого элемента по умолчанию — пустая строка.

Поскольку эти элементы добавляются в существующую аннотацию @Deprecated, программы обработки аннотаций будут видеть значения по умолчанию для forRemoval() и since(), если они обрабатывают class-файл, скомпилированный версией @Deprecated старше JDK 9.

Наличие аннотации @Deprecated у API — это сообщение от автора или сопровождающего API его пользователям. В самом общем смысле объявление устаревшим — это совет пользователям перевести свой код с устаревшего API, не добавлять зависимостей от этого API в новом коде или при сопровождении старого кода, либо указание на то, что сопровождение кода, зависящего от этого API, связано с определённым риском. Причин рекомендовать такой переход много. Среди них могут быть следующие:

  • API содержит изъяны, и исправлять их нецелесообразно,

  • использование API, скорее всего, приведёт к ошибкам,

  • API заменён другим API,

  • API вышел из употребления,

  • API является экспериментальным и может меняться несовместимым образом,

  • или любое сочетание перечисленного.

Точные причины объявления API устаревшим зачастую слишком тонки, чтобы выразить их флагами или значениями элементов аннотации. Настоятельно рекомендуется описывать причины объявления API устаревшим в документационных комментариях этого API. Кроме того, рекомендуется также обсуждать в документации возможные API на замену и давать на них ссылки.

Тем не менее одно конкретное значение-флаг предусмотрено. Логический элемент forRemoval(), если он равен true, указывает на намерение удалить элемент API в одном из будущих выпусков проекта. Тем самым пользователи API заранее предупреждены, что если они не откажутся от этого API, их код может перестать работать при переходе на более новый выпуск. Если forRemoval() равен false, это означает рекомендацию отказаться от устаревшего API, но без конкретного намерения удалить этот API.

Аннотация @Deprecated и javadoc-тег @deprecated должны у элемента API либо оба присутствовать, либо оба отсутствовать. Наличие одного без другого считается ошибкой. Флаг lint -Xlint:dep-ann компилятора javac выдаёт предупреждения, если тег @deprecated присутствует у API без аннотации @Deprecated. Для обратной ситуации предупреждения сейчас нет, см. JDK-8141234.

Аннотация @Deprecated не должна напрямую влиять на поведение устаревших API, а влияние на производительность должно быть пренебрежимо малым.

Использование в Java SE

Тип аннотации @Deprecated входит в Java SE, поэтому его можно применять к API любой библиотеки классов, использующей платформу Java SE. Точные правила и политику использования типа аннотации @Deprecated в этих библиотеках классов определяют их сопровождающие. Сопровождающим библиотек классов рекомендуется разработать и задокументировать такую политику.

В этом разделе описывается использование типа аннотации @Deprecated в самих API Java SE, а также политика, которая регулирует такое использование.

У ряда API Java SE аннотация @Deprecated будет добавлена, изменена или удалена. Ниже перечислены изменения, реализованные в Java SE 9. Если не указано иное, перечисленные здесь объявления устаревшими не предполагают удаления. Обратите внимание, что это не полный список объявлений устаревшими в Java SE 9.

  • добавить @Deprecated к конструкторам обёрток примитивных типов (Boolean, Integer и т. д.) (JDK-8145468)

  • добавить @Deprecated(forRemoval=true) к методам Runtime.traceInstructions и Runtime.traceMethodCalls (JDK-8153330)

  • добавить @Deprecated к различным классам java.applet и связанным с ними классам (JEP 289)

  • добавить @Deprecated к java.util.Observable и Observer (JDK-8154801)

  • добавить @Deprecated(forRemoval=true) к различным заменённым API безопасности, включая java.security.acl (JDK-8157847), javax.security.cert и com.sun.net.ssl (JDK-8157712), java.security.Certificate (JDK-8157707) и javax.security.auth.Policy (JDK-8157848)

  • добавить @Deprecated(forRemoval=true) к java.lang.Compiler (JDK-4285505)

  • добавить @Deprecated к нескольким модулям Java EE и модулю java.corba (JDK-8169069, JDK-8181195, JDK-8181702, JDK-8174728)

  • изменить уже устаревшие методы Thread.destroy(), Thread.stop(Throwable), Thread.countStackFrames(), System.runFinalizersOnExit() и различные неиспользуемые методы Runtime и SecurityManager, чтобы у них было @Deprecated(forRemoval=true) (JDK-8145468)

С учётом истории объявления устаревшим в Java SE и упора на долгосрочную совместимость API между версиями удаление API — серьёзный вопрос. Поэтому объявление устаревшим с элементом forRemoval=true следует применять только тогда, когда есть чёткий и определённый план удалить этот API в следующем выпуске платформы Java SE.

Элемент API не следует удалять из спецификации Java SE, если он не был поставлен с аннотацией @Deprecated(forRemoval=true) в одной из предыдущих версий Java SE. Допустимо сразу объявлять API устаревшим с forRemoval=true. Не обязательно сначала объявлять его устаревшим с forRemoval=false, а затем менять на forRemoval=true, прежде чем удалять API.

Для элементов API, объявленных устаревшими в Java SE 9 и позже, элемент since должен содержать строку версии Java SE, обозначающую версию, в которой элемент API был объявлен устаревшим. Строка версии должна соответствовать формату, заданному в JEP 223. Поскольку в Java SE изменения спецификации обычно вносятся только в основных выпусках, строка версии часто будет состоять только из номера версии «MAJOR». Таким образом, для элементов API, объявленных устаревшими в Java SE 9, значение элемента since должно быть просто «9».

Значение since у элементов API, объявленных устаревшими до Java SE 9, будет заполняться только по мере возможности. (Делать это для всех API почти бесполезно, это в основном упражнение в исторических изысканиях.) Строка для значения since в таких случаях должна соответствовать соглашениям о версиях JDK, принятым для javadoc-тега @since в этих выпусках: обычно от 1.0 до 1.8, но иногда с номером «micro»-выпуска, например 1.0.2. Инструменты обработки аннотаций, которые ищут это значение у API Java SE и находят пустую строку, должны считать, что API был объявлен устаревшим в Java SE 8 или раньше.

Объявление API устаревшими увеличит число обязательных предупреждений, с которыми сталкиваются проекты при сборке с новыми версиями Java SE. Некоторые проекты, в том числе сам JDK, собираются с параметрами компилятора, которые включают подробные предупреждения и превращают предупреждения в ошибки. Для таких проектов появление устаревших API в Java SE может привести к большому числу предупреждений и значительно увеличить трудозатраты на переход на новую версию Java SE. Существующих механизмов управления предупреждениями, таких как аннотация @SuppressWarnings и параметры командной строки компилятора, для решения этой проблемы недостаточно. Фактически это ограничивает, какие API можно объявить устаревшими в конкретном выпуске Java SE, и делает объявление устаревшими неактуальных, но популярных API практически невозможным. Поэтому в будущем потребуется работа по улучшению механизмов управления предупреждениями об устаревании.

Влияние forRemoval на политику предупреждений

The Java Language Specification, раздел 9.6.4.6 предписывает определённое поведение предупреждений, которое зависит от статуса устаревания API, от которого зависит код («место объявления»), в сочетании со статусом устаревания кода, использующего этот API («место использования»). Добавление элемента forRemoval добавляет ещё один набор случаев, которые нужно определить. Для краткости будем называть объявление устаревшим с forRemoval=false «обычным устареванием», а объявление устаревшим с forRemoval=true — «окончательным устареванием».

В Java SE 8 и раньше forRemoval не существовало, поэтому единственным видом устаревания было обычное устаревание. Выдавалось ли предупреждение об устаревании, зависело от статуса устаревания и места использования, и места объявления. Вот таблица случаев, существовавших в Java SE 8:

use site     | API declaration site
    context      | not dep.   deprecated
                 +-----------------------
    not dep.     |    N          W
                 |
    deprecated   |    N          N (1)

        N = no warning
        W = warning

(Примечание 1) Это странный случай. Если и место использования, и место объявления объявлены устаревшими, предупреждение не выдаётся. Это имеет смысл, если оба места находятся в одной библиотеке классов, которая сопровождается и выпускается как единое целое. Поскольку они сопровождаются вместе, выдавать предупреждение в этом случае мало смысла. Однако если место использования находится в библиотеке классов, которая сопровождается отдельно от места объявления, они могут развиваться с разной скоростью, и тогда отсутствие предупреждения в этом случае, скорее всего, является недостатком. Тем не менее этот механизм был полезен для сокращения числа предупреждений при компиляции JDK до появления аннотации @SuppressWarnings в Java SE 5.

(JLS 9.6.4.6 также требует не выдавать предупреждений, если место использования находится в том же самом внешнем классе, что и место объявления. В таких случаях места использования и объявления по определению сопровождаются вместе, так что довод в пользу отсутствия предупреждения здесь вполне применим.)

В Java SE 9 с появлением forRemoval добавляется несколько новых случаев, связанных с окончательным устареванием. Для этого нужно ввести новый вид предупреждения.

Предупреждения, выдаваемые в месте использования API с обычным устареванием, — это «обычные предупреждения об устаревании», такие же, как в Java SE 8 и раньше. По сложившейся ранее привычке их часто называют просто «предупреждениями об устаревании».

Предупреждения, выдаваемые в месте использования API с окончательным устареванием, формально можно было бы назвать «предупреждениями об окончательном устаревании», но это довольно громоздко. Вместо этого будем называть такие предупреждения «предупреждениями об удалении».

Предлагаемая таблица случаев приведена ниже:

use site     |      API declaration site
    context      | not dep.   ord. dep.   term. dep.
                 +----------------------------------
    not dep.     |    N         oW (2)       rW (5)
                 |
    ord. dep.    |    N          N (3)       rW (6)
                 |
    term. dep.   |    N          N (4)       rW (7)

(Примечание 2) «oW» означает «обычное предупреждение об устаревании» — тот же вид предупреждения, который выдавался в этом случае в Java SE 8 и раньше.

(Примечание 3) Четыре элемента в левом верхнем углу такие же, как в таблице для Java SE 8, из соображений обратной совместимости.

(Примечание 4) Предупреждение здесь не выдаётся по аналогии с совместимым поведением. Если и место использования, и место объявления имеют обычное устаревание, было бы нелепо, если бы перевод места использования в окончательное устаревание приводил к появлению предупреждения. Поэтому в этом случае предупреждение не выдаётся.

(Примечание 5) «rW» означает «предупреждение об удалении». Все предупреждения, выдаваемые в местах использования API с окончательным устареванием, являются предупреждениями об удалении.

(Примечание 6) Этот случай весьма важен. Мы хотим, чтобы использование API с окончательным устареванием всегда приводило к предупреждению об удалении, даже если место использования находится в устаревшем коде.

(Примечание 7) Это похоже на (6). Можно подумать, что раз и место использования, и место объявления имеют окончательное устаревание, оба «скоро исчезнут» и выдавать здесь предупреждение бессмысленно. Но возможно, что место объявления находится в библиотеке, которая развивается быстрее, чем место использования, и тогда место использования может пережить место объявления. Поэтому предупреждение о предстоящем удалении места объявления необходимо.

Общее правило для четырёх элементов в правом нижнем углу таково. Если место использования объявлено устаревшим, обычным или окончательным образом, обычные предупреждения об устаревании выдаваться не будут, но предупреждения об удалении по-прежнему будут выдаваться.

Пример обычного предупреждения об устаревании может выглядеть так:

UseSite.java:3: warning: [deprecation] ordinary() in DeclSite has been deprecated

Пример предупреждения об удалении может выглядеть так:

UseSite.java:4: warning: [removal] removal() in DeclSite has been deprecated and marked for removal

Конкретная формулировка предупреждений и механизмы их настройки могут отличаться от компилятора к компилятору.

Подавление предупреждений об устаревании

В Java SE 8 и раньше предупреждения об устаревании можно было подавить, пометив место использования аннотацией @SuppressWarnings("deprecation"). При наличии окончательного устаревания это поведение нужно изменить.

Рассмотрим случай, когда место использования зависит от API с обычным устареванием, а полученное предупреждение подавлено аннотацией @SuppressWarnings("deprecation"). Если место объявления изменить так, чтобы оно получило окончательное устаревание, мы хотели бы, чтобы в месте использования появилось предупреждение об удалении, несмотря на то что предупреждения в месте использования уже подавлены. Если бы в этом случае новое предупреждение не выдавалось, API мог бы получить окончательное устаревание, а затем быть удалён без каких-либо предупреждений в местах его использования.

Следующий сценарий иллюстрирует проблему. Предположим, что аннотация @SuppressWarnings("deprecation") подавляла бы и обычные предупреждения об устаревании, и предупреждения об удалении. Тогда могло бы произойти следующее:

  1. Место использования X зависит от API Y, который пока не объявлен устаревшим
  2. Объявление Y меняется на обычное устаревание, и в X выдаётся обычное предупреждение об устаревании
  3. X помечается аннотацией @SuppressWarnings("deprecation"), и предупреждение подавляется
  4. Объявление Y меняется на окончательное устаревание; предупреждение об удалении в X по-прежнему подавлено
  5. Y полностью удаляется, и X неожиданно перестаёт работать

Поскольку цель объявления устаревшим — сообщать информацию об эволюции API, в частности об удалении API, отсутствие какого-либо предупреждения в этом случае является серьёзной проблемой. Отсюда следует, что предупреждение должно выдаваться, когда устаревание «повышается» с обычного до окончательного, даже если предупреждения в этом месте использования ранее были подавлены.

Нам нужен механизм подавления предупреждений об удалении, отличный от механизма, который сейчас используется для подавления обычных предупреждений об устаревании. Решение — использовать в аннотации @SuppressWarnings другую строку.

Предупреждения об удалении — предупреждения, возникающие при использовании API с окончательным устареванием, — можно подавить аннотацией

@SuppressWarnings("removal")

Эта аннотация подавляет только предупреждения об удалении, но не обычные предупреждения об устаревании. Мы рассматривали вариант сделать её сильной формой подавления, которая охватывала бы и обычные предупреждения об устаревании, и предупреждения об удалении. Однако это потенциально ведёт к ошибкам. Программисты могли бы использовать @SuppressWarnings("removal") для подавления предупреждений об обычном устаревании. Тогда предупреждения не появились бы, если бы обычное устаревание сменилось окончательным, и это привело бы к неожиданной поломке, когда API с окончательным устареванием в итоге удалят.

Как и раньше, предупреждения от использования API с обычным устареванием можно подавить аннотацией

@SuppressWarnings("deprecation")

Как отмечено выше, эта аннотация подавляет только обычные предупреждения об устаревании; предупреждения об удалении она не подавляет.

Если в каком-то месте нужно подавить и обычные предупреждения об устаревании, и предупреждения об удалении, можно использовать следующую конструкцию:

@SuppressWarnings({"deprecation", "removal"})

Ниже приведена копия таблицы предупреждений из предыдущего раздела, изменённая так, чтобы показать, как можно подавить предупреждения в разных случаях.

use site     |      API declaration site
    context      | not dep.   ord. dep.   term. dep.
                 +----------------------------------
    not dep.     |    -        @SW(d)       @SW(r)
                 |
    ord. dep.    |    -           -         @SW(r)
                 |
    term. dep.   |    -           -         @SW(r)

        @SW(d) = @SuppressWarnings("deprecation")
        @SW(r) = @SuppressWarnings("removal")

Если предупреждение об удалении подавлено с помощью @SuppressWarnings("removal") в месте использования API с окончательным устареванием, а затем этот API переводится в обычное устаревание, появление обычного предупреждения об устаревании выглядит несколько странно. Однако мы ожидаем, что путь эволюции API от окончательного устаревания обратно к обычному будет встречаться весьма редко.

Раздел JLS 9.6.4.6 нужно будет соответствующим образом изменить. Это изменение рассматривается в JDK-8145716.

Статический анализ

Будет предоставлен инструмент статического анализа jdeprscan, который сканирует jar-файл (или другой набор class-файлов) на предмет использования устаревших элементов API. По умолчанию устаревшими API будут считаться те, что объявлены устаревшими в самой Java SE. Будущее расширение позволит искать устаревшие элементы, объявленные в библиотеке классов, отличной от Java SE.

Идеи для дальнейшей работы

Можно было бы предоставить инструмент динамического анализа jdeprdetect для отслеживания динамического использования устаревших API. Его можно реализовать с помощью Java-агента, который инструментирует устаревшие элементы API и выдаёт предупреждения, когда во время выполнения обнаруживается использование этих элементов.

Динамический анализ должен помогать выявлять случаи, которые пропускает статический анализ. К ним относятся рефлексивный доступ к устаревшим API или использование устаревших провайдеров, загруженных через ServiceLoader. Кроме того, динамический анализ может показать отсутствие зависимости, которую мог бы отметить статический анализ. Например, код может ссылаться на устаревший API, и из-за этой ссылки jdeprscan выдаст предупреждение. Однако если код, ссылающийся на устаревший API, является мёртвым кодом, jdeprdetect предупреждения не выдаст. Эта информация должна помочь разработчикам расставить приоритеты при миграции кода.

Некоторые возможности целиком находятся внутри реализаций библиотек и не проявляются ни в каких публичных API. Один из примеров — алгоритм «legacy merge sort». Подробнее см. Java SE 7 and JDK 7 Compatibility. Библиотечные реализации устаревших возможностей должны иметь возможность проверять различные системные свойства, чтобы определить, выдавать ли сообщения в журнал во время выполнения и, если да, в какой форме. Среди этих свойств могут быть:

  • java.deprecation.enableLoggingboolean, по умолчанию false

    Если значение истинно (что определяется методом Boolean.parseBoolean), библиотечный код будет записывать в журнал сообщения об устаревании. Сообщения будут записываться с помощью логгера, полученного вызовом System.getLogger(), с уровнем System.Logger.Level.WARNING.

  • java.deprecation.enableStackTraceboolean, по умолчанию false

    Если значение истинно и журналирование устаревания включено, сообщения журнала будут содержать трассировку стека.

Реализация и улучшения других инструментов выходят за рамки этого JEP. Здесь описан ряд идей таких улучшений инструментов в качестве предложений для дальнейшей работы.

Инструмент javadoc можно было бы доработать, чтобы он обрабатывал подробный код аннотации @Deprecated. Он также мог бы заметнее отображать значения Detail. Обработка Javadoc-тега @deprecated должна остаться в основном без изменений, хотя, возможно, её стоит немного изменить, чтобы включать информацию о значениях forRemoval и since.

Стандартный doclet можно было бы изменить, чтобы он обрабатывал устаревшие API иначе. Например, устаревшие члены класса можно было бы вынести на отдельную вкладку рядом с существующими вкладками для экземплярных, абстрактных и конкретных методов. Устаревшие классы можно было бы перенести в отдельный раздел во фрейме пакета. Сейчас в нём есть разделы Interfaces, Classes, Enums, Exceptions, Errors и Annotation Types. Можно было бы добавить новые разделы для устаревших членов.

Список устаревших API тоже можно было бы улучшить. (На эту страницу ведёт ссылка в самом верху каждой страницы, на панели со ссылками Overview, Package, Class, Use, Tree, Deprecated, Index, Help.) Сейчас эта страница упорядочена по видам: интерфейсы, классы, исключения, типы аннотаций, поля, методы, конструкторы и элементы типов аннотаций. Элементы API, у которых указано значение forRemoval=true, следует выделять, поскольку их предстоящее удаление потенциально может иметь большие последствия.

Улучшенная аннотация @Deprecated повлияет и на другие инструменты, например на IDE. Так, устаревшие API по умолчанию не должны появляться в меню и диалогах автодополнения IDE. Или же IDE могли бы предлагать правила автоматического рефакторинга, которые заменяют вызовы устаревших API вызовами API, пришедших им на смену.

Альтернативы

Среди предложенных альтернатив были такие: останавливать JVM, отключать устаревшие возможности или считать использование устаревших API ошибкой компиляции, если не указан параметр для конкретной версии. Все эти предложения позволят сообщить разработчику только о первом использовании устаревшей возможности, потому что в этот момент обычный ход выполнения программы (или сборки) прерывается. Поэтому последующие случаи использования устаревших возможностей, скорее всего, останутся незамеченными. Столкнувшись с такими сбоями, большинство разработчиков просто укажут параметр для конкретной версии, чтобы включить устаревшие возможности. Поэтому в целом такой подход не позволит сообщить разработчикам обо всех устаревших возможностях, которые использует приложение.

Предлагалось упразднить Javadoc-тег @deprecated в пользу аннотации @Deprecated. Javadoc-тег @deprecated и аннотация @Deprecated всегда должны либо присутствовать обе, либо отсутствовать обе. Однако они избыточны лишь в очень абстрактном, концептуальном смысле. Javadoc-тег @deprecated содержит описательный текст, обоснование, а также информацию об API на замену и ссылки на них. Эта информация вполне подходит для включения в документацию javadoc, в которой уже есть средства для этого (например, теги ссылок). Перенос такой текстовой информации в значения аннотаций потребовал бы, чтобы javadoc извлекал информацию из аннотаций, а не из комментариев документации. Разработчикам было бы сложнее её поддерживать, поскольку в аннотациях нет поддержки разметки. Наконец, элементы аннотаций занимают место во время выполнения, а присутствие текста документации в памяти во время выполнения не нужно.

В качестве подробного кода предлагалось строковое значение. На первый взгляд это даёт больше гибкости, но при этом создаёт проблемы из-за слабой типизации и конфликтов пространств имён, что может приводить к незамеченным ошибкам.

В ранних версиях этого предложения в аннотации @Deprecated был элемент «replacement». Предполагалось, что он будет указывать конкретный API, который заменяет устаревший. На практике ни у одного устаревшего API никогда не бывает API, способного заменить его без изменений: всегда есть компромиссы и проектные соображения или приходится выбирать между несколькими возможными заменами. Все эти вопросы требуют обсуждения, поэтому лучше подходят для текстовой документации. Наконец, нет синтаксиса, позволяющего сослаться на другой API из элемента аннотации, тогда как Javadoc уже поддерживает такие ссылки с помощью тегов @see и @link.

Предыдущие версии этого предложения включали разнообразные коды «причины», в том числе UNSPECIFIED, DANGEROUS, OBSOLETE, SUPERSEDED, UNIMPLEMENTED и EXPERIMENTAL. С их помощью пытались закодировать причину, по которой API стал устаревшим, риски его использования, а также наличие API на замену. На практике вся эта информация слишком субъективна, чтобы кодировать её значениями аннотации. Вместо этого её следует описывать в документирующем комментарии Javadoc. Единственная существенная деталь, которая остаётся, — есть ли намерение удалить API. Она выражается элементом аннотации forRemoval.

Тестирование

Для нового инструментария будет создан достаточно простой набор тестов. Будет подготовлен набор случаев, в которых каждый вид элемента API, который может быть объявлен устаревшим, объявлен устаревшим. Будет создан ещё один набор случаев, состоящий из использований каждого устаревшего API из описанных выше случаев. Следует запустить статический анализатор jdeprscan и убедиться, что он выдаёт предупреждения для всех таких использований.