Summary

Revamp the @Deprecated annotation, and provide tools to strengthen the
API life cycle.

Goals

Provide better information about the status and intended disposition
of APIs in the specification.

Provide a tool to analyze an application's static usage of deprecated APIs.

Non-Goals

It is not a goal of this project to unify the @deprecated Javadoc
tag with the @Deprecated annotation.

Motivation

Deprecation is a technique to communicate information about the life cycle of an API:
to encourage applications to migrate away from the API, to discourage applications
from forming new dependencies on the API, and to inform developers of the risks of
continuing dependence upon the API.

Java offers two mechanisms to express deprecation: the @deprecated Javadoc tag,
introduced in JDK 1.1, and the @Deprecated annotation, introduced in Java SE 5.
The API specification for the @Deprecated annotation, mirrored in The Java Language
Specification, is:

A program element annotated @Deprecated is one that programmers
are discouraged from using, typically because it is dangerous, or
because a better alternative exists. Compilers warn when a
deprecated program element is used or overridden in non-deprecated
code.

However, the @Deprecated annotation ended up being used for several
different purposes. Very few deprecated APIs were actually removed,
leading some people to believe that nothing would ever be removed.
On the other hand, other people believed that everything that was deprecated
might eventually be removed, which was never the intent either. (Although it
wasn't stated explicitly in the specifications, various documents mentioned that
deprecated APIs would be removed at some point.) This resulted in an unclear
message being delivered to developers about the meaning of @Deprecated,
and what, if anything, developers should do when they encountered usage of a
deprecated API. Everybody was confused about what deprecation actually meant,
and nobody took it seriously. This in turn has made it difficult ever to remove
anything from the Java SE API.

Another problem with deprecation is that warnings are issued only at
compile time. As APIs become deprecated in successive versions of Java
SE, existing binaries continue to depend and use the deprecated APIs
with no warnings. If a deprecated API were to be removed in a JDK
release, even after one or more releases where it was deprecated, this
would come as an unpleasant surprise to users of old application
binaries. The application would suddenly fail with a linkage error,
with no warnings having ever been emitted. Worse, there is no means
for developers to check whether existing binaries have any
dependencies on deprecated APIs. This causes significant tension
between the ability to run old binaries on new JDK releases versus
the need to evolve the specification through the retirement of old
APIs.

In summary, the deprecation mechanisms have been applied inconsistently
in the Java SE API, resulting in confusion about the meaning of deprecation
in principle and the proper use of deprecation in practice.

Description

Specifications

The primary purpose of enhancing the @Deprecated annotation is to
provide finer-grained information to tools about the deprecation
status of an API. These tools in turn use the annotation to report
information to users of the API. The @Deprecated annotation has
runtime retention and therefore consumes heap memory. The information
here should therefore be minimal and well-specified.

The following elements are to be added to the java.lang.Deprecated
annotation type:

A method forRemoval() returning a boolean. If true, it means that this API
element is earmarked for removal in a future release. If false, the API element
is deprecated, but there is currently no intention to remove it in a future release.
The default value of this element is false.

A method named since() returning String. This string should
contain the release or version number at which this API became
deprecated. It has free-form syntax, but the release numbering should
follow the same scheme as the @since Javadoc tag for the project
containing the deprecated API. Note that this value is not redundant
with the Javadoc @since tag, because that records the release in
which the API was introduced, whereas the since() method in a
@Deprecated annotation records the release in which the API was
deprecated. The default value of this element is the empty string.

Since these elements are being added to the existing @Deprecated
annotation, annotation processing programs will see the default values
for forRemoval() and since() if they are processing a class file
that was compiled with a version of @Deprecated older than JDK 9.

The presence of the @Deprecated annotation on an API is
communication from the author or maintainer of the API to users of the
API. Most generally, deprecation is advice that users migrate their
usage away from the deprecated API, that they avoid adding
dependencies on this API from new code or while maintaining old code,
or that there is a certain amount of risk in maintaining code that
depends on this API. There are many reasons to recommend such
migration. Reasons might include the following:

the API is flawed and is impractical to fix,

usage of the API is likely to lead to errors,

the API has been superseded by another API,

the API is obsolete,

the API is experimental and is subject to
incompatible changes,

or any combination of the above.

The exact reasons for deprecating an API are often too subtle to be
expressed as flags or element values in the annotation. It is strongly
recommended that the reasons for deprecating an API be described in
that API's documentation comments. In addition, it is also recommended
that potential replacement APIs be discussed and linked from the
documentation.

One specific flag value is provided, however. The forRemoval() boolean
element, if true, indicates intent that the API element is to be removed in
some future release of the project. Users of the API are thus given
advance warning that, if they don't migrate away from the API, their
code is liable to break when upgrading to a newer release. If
forRemoval() is false, this indicates a recommendation to migrate away
from the deprecated API, but without any specific intent to remove that
API.

The @Deprecated annotation and the @deprecated javadoc tag should
both be present or both be absent on an API element. The presence of
one without the other is considered to be a mistake. The javac lint
flag -Xlint:dep-ann will issue warnings if the @deprecated tag is
present on an API that lacks the @Deprecated annotation. There is
currently no warning if the reverse is true; see
JDK-8141234.

The @Deprecated annotation should have no direct impact on the
behavior of deprecated APIs, and there should negligible performance
impact.

Usage in Java SE

The @Deprecated annotation type appears in Java SE, and thus it may
be applied to the APIs any class library that uses the Java SE platform.
The exact rules and policies for how those class libraries use the
@Deprecated annotation type is a matter for the maintainers of those
libraries to determine. It is recommended that class library maintainers
develop and document such policies.

This section describes the uses of the @Deprecated annotation type on
Java SE APIs themselves and also the policies governing such use.

Several Java SE APIs will have a @Deprecated annotation
added, updated, or removed. The changes implemented in Java SE 9 are listed below.
Unless otherwise specified, the deprecations listed here are not for removal.
Note that this is not a comprehensive list of deprecations in Java SE 9.

Given the history of deprecation in Java SE, and the emphasis on long term
API compatibility across versions, removal of an API is a matter of serious
concern. Therefore, deprecation with the element forRemoval=true should
be applied only when there is a clear and definite plan for removing that
API in the next release of the Java SE platform.

An API element should not be removed from the Java SE specification unless
it has been delivered with an annotation of @Deprecated(forRemoval=true)
in a previous version of Java SE. It is acceptable for a deprecation to be
introduced with forRemoval=true. It isn't necessary to first deprecate with
forRemoval=false, then upgrade to forRemoval=true, before removing the API.

For API elements deprecated in Java SE 9 and beyond, the since
element should contain the Java SE version string denoting the version
in which the API element was deprecated. The version string should
conform to the format specified in JEP 223.
Since Java SE typically makes specification changes only in major releases,
the version string will often consist solely of the "MAJOR" version number.
Thus, for API elements deprecated in Java SE 9, the since element value
should simply be "9".

API elements that had been deprecated prior to Java SE 9 will have
their since value filled in only as time permits. (Doing this for
all APIs is of marginal value and is mainly an exercise in historical
research.) The string used for the since value in such cases should
conform to the JDK version conventions used for the @since javadoc tag
for those releases, typically 1.0 through 1.8 but sometimes with a "micro"
release number, such as 1.0.2. Annotation processing tools looking for this
value on Java SE APIs and finding an empty string should assume
that the deprecation occurred in Java SE 8 or earlier.

Deprecating APIs will increase the number of mandatory warnings that projects encounter
when building against newer versions of Java SE. Some projects, including the JDK itself,
build with compiler options that enable verbose warnings and that turn warnings into
errors. For such projects, adding deprecated APIs to Java SE can introduce a large number
of warnings, adding significantly to the effort of migrating to a new version of Java SE.
Existing mechanisms for managing warnings, such the @SuppressWarnings annotation and
compiler command-line options, are insufficient for dealing with this issue. This effectively
places a limit on which APIs can be deprecated in a given Java SE release, and it makes
deprecation of obsolete but popular APIs nearly impossible. This calls for a future effort
to enhance the mechanisms available to manage deprecation warnings.

Impact of forRemoval on Warning Policy

The Java Language Specification, section 9.6.4.6
mandates specific warning behaviors that depend upon the deprecation status of an
API that is being depended upon (the "declaration site"), in combination with the
deprecation status of the code that is using that API (the "use site"). The addition
of the forRemoval element adds another set of cases that must be defined. For the
sake of brevity, we will refer to a deprecation with forRemoval=false as an
"ordinary deprecation" and a deprecation with forRemoval=true as a
"terminal deprecation."

In Java SE 8 and earlier, forRemoval did not exist, so the only kind of deprecations
were ordinary deprecations. Whether a deprecation warning was issued depended upon
the deprecation status of both the use site and the declaration site.
Here is a table of cases that existed in Java SE 8:

(Note 1) This is an odd case. If the use and declaration site are both deprecated, no warning
is issued. This makes sense if both sites are within a single class library that is maintained
and released as a unit. Since they are maintained together, there is little point in issuing a
warning in this case. However, if the use site is within a class library that is maintained
separately from the declaration site, they may evolve at different rates, and so not issuing a
warning in this case is likely to be a misfeature. However, this mechanism was useful for
reducing the number of warnings from compilation of the JDK, prior to the introduction of the
@SuppressWarnings annotation in Java SE 5.

(JLS 9.6.4.6 also requires no warnings to be issued if the use site is within the
same outermost class as the declaration site. In such cases the use and declaration
sites are by definition maintained together, so the rationale for not
issuing a warning applies well.)

In Java SE 9, the introduction of forRemoval adds several new cases having to
do with terminal deprecation. This requires the introduction of a new kind of warning.

Warnings issued at the point of use of an ordinarily deprecated API are "ordinary deprecation
warnings" which are the same as in Java SE 8 and earlier. These are often simply called
"deprecation warnings" as a holdover from previous usage.

Warnings issued at the point of use of a terminally deprecated API might formally be called
"terminal deprecation warnings" but this is rather verbose. Instead we will refer to such
warnings as "removal warnings".

(Note 2) "oW" refers to an "ordinary deprecation warning" which is the same kind
of warning that has occurred in this case in Java SE 8 and earlier.

(Note 3) The upper left four elements are the same as in the Java SE 8
table, for reasons of backward compatibility.

(Note 4) No warning is issued here by extrapolating from compatible behavior.
If both use and declaration site are both ordinarily deprecated, it would be
perverse if changing the use site to be terminally deprecated were to introduce
a warning. Thus, no warning is issued in this case.

(Note 5) "rW" refers to a "removal warning". All warnings issued at use sites of
terminally deprecated APIs are removal warnings.

(Note 6) This case is quite significant. We always want the use of a terminally deprecated
API to generate a removal warning, even if the use site is within deprecated code.

(Note 7) This is similar to (6). One might think that, since both the use and
declaration sites are terminally deprecated, both are "going away" and that it
would be pointless to issue a warning here. But the possibility is that the declaration
site is within a library that is evolving more quickly than the use site, so the use
site might outlive the declaration site. Therefore, a warning about the impending removal
of the declaration site is necessary.

The general rule that covers the lower right four elements is as follows. If the use site
is deprecated, whether ordinarily or terminally, no ordinary deprecation warnings will be
issued, but removal warnings will still be issued.

An example of an ordinary deprecation warning might be as follows:

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

An example of a removal warning might be as follows:

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

The specific wording of the warnings, and the mechanisms for customization of warnings, may differ
from compiler to compiler.

Suppression of Deprecation Warnings

In Java SE 8 and earlier, it was possible to suppress deprecation warnings
by annotating the use site with @SuppressWarnings("deprecation"). This behavior
needs to be modified in the presence of terminal deprecation.

Consider a case where a use site depends on an API that is
ordinarily deprecated, and that the resulting warning has been suppressed
with a @SuppressWarnings("deprecation") annotation. If the declaration site were
to be modified to be terminally deprecated,
we would want a removal warning to occur at the use site, even though warnings
at the use site have already been suppressed. If a new warning were not
issued in this case, it would be possible for an API to be terminally
deprecated and then removed without any warnings at its use sites.

The following scenario illustrates the problem. Suppose that the
@SuppressWarnings("deprecation") annotation were to suppress both ordinary
deprecation warnings as well as removal warnings. Then, the following
could occur:

Inasmuch as the purpose of deprecation is to communicate information about API evolution,
particularly about removal of APIs, the lack of any warning in this case is a serious problem.
It follows that a warning should be given when a deprecation is "upgraded"
from an ordinary to a terminal deprecation, even if the warnings at that use site had
previously been suppressed.

We need a mechanism for suppressing removal warnings that differs
from the mechanism currently used for suppressing ordinary deprecation warnings.
The solution is to use a different string in the @SuppressWarnings annotation.

Removal warnings -- warnings that arise from the use of terminally deprecationed APIs --
can be suppressed with the annotation

@SuppressWarnings("removal")

This annotation suppresses only removal warnings, and not ordinary deprecation warnings.
We considered making this be a strong form of suppression that would cover both ordinary
deprecation warnings and removal warnings. However, this potentially leads to errors. Programmers
might use @SuppressWarnings("removal") to suppress warnings from ordinary deprecations.
This would prevent warnings from appearing if an ordinary deprecation were changed to a
terminal deprecation, leading to unexpected breakage when the terminally deprecated API
is eventually removed.

As before, warnings from the use of ordinarily deprecated APIs can be suppressed with the annotation

If a removal warning is suppressed with @SuppressWarnings("removal") at the use site of a
terminally deprecated API, and that API is changed to an ordinary deprecation, it is
somewhat odd that an ordinary deprecation warning will appear. However, we expect the evolution
path of an API from terminal deprecation back to ordinary deprecation to be quite rare.

JLS section 9.6.4.6 will need to be modified accordingly. That change is covered by
JDK-8145716.

Static Analysis

A static analysis tool jdeprscan will be provided that scans
a jar file (or some other aggregation of class files) for uses of
deprecated API elements. By default, the deprecated APIs will be
the deprecations from Java SE itself. A future extension will provide
for the ability to scan for deprecations that have been declared
in a class library other than Java SE.

Ideas for Future Work

A dynamic analysis tool jdeprdetect could be provided to track dynamic
uses of deprecated APIs. It can be implemented by
using a Java agent, instrumenting the deprecated API elements
and issuing warning messages when usage of those elements is detected at
runtime.

Dynamic analysis should be helpful at catching cases that static
analysis misses. These cases include reflective access to deprecated
APIs, or use of deprecated providers loaded via
ServiceLoader. Furthermore, dynamic analysis can show the absence
of a dependency that might be flagged by static analysis. For example,
code might reference a deprecated API, and this reference will cause
jdeprscan to emit a warning. However, if the code referencing a
deprecated API is dead code, no warning will be emitted by
jdeprdetect. This information should help developers prioritize their
code migration efforts.

Certain features reside entirely within library implementations and
aren't manifested in any public APIs. One example of this is the
"legacy merge sort" algorithm. See
Java SE 7 and JDK 7 Compatibility
for further information. Library implementations of deprecated
features should be able to check various system properties to determine whether
to issue log messages at runtime, and if so, what form the log message
should take. These properties might include:

java.deprecation.enableLogging — boolean, default false

If true, as determined by the Boolean.parseBoolean method, then
library code will log deprecation messages. Messages will be
logged using a logger obtained by calling System.getLogger(),
and messages will be logged using a level of
System.Logger.Level.WARNING.

java.deprecation.enableStackTrace — boolean, default false

If true, and if deprecation logging is enabled, log messages
will include a stack trace.

Implementation and enhancements to other tools is beyond the scope of
this JEP. A number of ideas for such tool enhancements are described
here as suggestions for future work.

The javadoc tool could be enhanced to handle the detail code of a
@Deprecated annotation. It could also provide a more prominent
display of the Detail values. The handling of the @deprecated
Javadoc tag should be largely unchanged, though perhaps it might be
modified somewhat to include information about the forRemoval and
since values.

The standard doclet could be modified to treat deprecated APIs
differently. For example, deprecated members of a class might be put
into a separate tab, along side the existing tabs for instance,
abstract, and concrete methods. Deprecated classes could be moved to a
separate section in the package frame. Currently, it contains sections
for Interfaces, Classes, Enums, Exceptions, Errors, and Annotation
Types. New sections for deprecated members could be added.

The list of deprecated APIs could be enhanced as well. (This page is
reached via the link at the very top of each page, in the bar
containing links Overview, Package, Class, Use, Tree, Deprecated,
Index, Help.) This page is currently organized by kind: interfaces,
classes, exceptions, annotation types, fields, methods, constructors,
and annotation type elements. API elements that include the value
forRemoval=true should be highlighted, as their impending
removal potentially has great impact.

The enhanced @Deprecated annotation will impact other tools such as
IDEs. For example, deprecated APIs should be absent from IDEs'
auto-completion menus and dialogs by default. Or, automatic
refactoring rules could be offered that replace calls to deprecated
APIs with calls to their replacements.

Alternatives

A set of alternatives that has been proposed includes having the JVM
halt, having deprecated features be disabled, or having usage of
deprecated APIs cause a compile-time error, unless a version-specific
option is supplied. All of these proposals will succeed only at
notifying the developer of the first usage of a deprecated feature,
because the normal program (or build) flow is interrupted at that
point. Thus, subsequent uses of deprecated features would likely go
undetected. Upon encountering such failures, most developers would
simply supply the version-specific option to enable the deprecated
features. Thus, in general, this approach won't be successful at
providing developers information about all of the deprecated
features in use by an application.

It has been suggested that the @deprecated Javadoc tag be retired in
favor of the @Deprecated annotation. The @deprecated Javadoc tag
and the @Deprecated annotation should always both be present or
absent. However, they are redundant only in very abstract, conceptual
sense. The @deprecated Javadoc tag provides descriptive text,
rationale, and information and links to replacement APIs. This
information is quite suitable for including in javadoc documentation,
which already has facilities for it (such as link tags). Moving such
textual information into annotation values would require javadoc to
extract the information from annotations instead of doc comments. It
would be harder for developers to maintain, since annotations have no
markup support. Finally, annotation elements take up space at runtime,
and it's unnecessary for documentation text to be present in memory at
runtime.

A string value has been proposed as a detail
code. This appears to provide more flexibility, but it also introduces
problems with weak typing and namespace conflicts, possibly leading to
undetected errors.

A "replacement" element in the @Deprecated annotation was present in
earlier versions of this proposal. The intent was for it to denote a
specific API that replaces the one being deprecated. In practice,
there is never a drop-in replacement API for any deprecated API; there
are always tradeoffs and design considerations, or choices
to be made among several possible replacements. All of these topics
require discussion and are thus better suited for textual
documentation. Finally, there is no syntax for referring to another
API from an annotation element, whereas Javadoc already supports such
references via its @see and @link tags.

Previous versions of this proposal included a variety of "reason"
codes including UNSPECIFIED, DANGEROUS, OBSOLETE, SUPERSEDED,
UNIMPLEMENTED, and EXPERIMENTAL. These attempted to encode the reason
for which an API was deprecated, the risks of using it, and also whether
a replacement API is available. In practice, all of this information is too
subjective be encoded as values in an annotation. Instead, this
information should be described in the Javadoc documentation comment.
The only significant bit of detail remaining is whether there is intent
to remove the API. This is expressed in the forRemoval annotation element.

Testing

A reasonably simple set of tests will be constructed for the new
tooling. A set of cases will be provided where each different kind of
API element that can be deprecated is deprecated. Another set of cases
will be constructed, consisting of usages of each deprecated API from
the cases described above. The static analysis checker jdeprscan should
be run to ensure that it issues warnings for all such usages.