JEP 277: Enhanced Deprecation
|Component||core-libs / java.lang|
|Discussion||jdk9 dash dev at openjdk dot java dot net|
|Reviewed by||Alex Buckley, Mark Reinhold|
|Endorsed by||Brian Goetz|
@Deprecated annotation, and provide tools to strengthen the
API life cycle.
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.
It is not a goal of this project to unify the
tag with the
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
A program element annotated
@Deprecatedis 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.
@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
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.
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
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
A method named
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
@sinceJavadoc tag for the project containing the deprecated API. Note that this value is not redundant with the Javadoc
@sincetag, because that records the release in which the API was introduced, whereas the
since()method in a
@Deprecatedannotation 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
annotation, annotation processing programs will see the default values
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
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
false, this indicates a recommendation to migrate away
from the deprecated API, but without any specific intent to remove that
@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
-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
@Deprecated annotation should have no direct impact on the
behavior of deprecated APIs, and there should negligible performance
Usage in Java SE
@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
added, updated, or removed. Some proposed changes 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.
Also note that several of these items will not be implemented in Java SE 9.
Check the status of the linked bug for details. (If there is no linked bug, the
deprecation has not been implemented.)
@Deprecatedto constructors for boxed primitives (
Integer, etc.) (JDK-8145468)
java.appletand related classes (JEP 289)
@Deprecated(forRemoval=true)to various superseded security APIs, including
@Deprecatedto "legacy collection" implementations
Calendar, and related classes (JDK-8164898)
modify already-deprecated methods
System.runFinalizersOnExit(), and various disused
SecurityManagermethods to have
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
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
in a previous version of Java SE. It is acceptable for a deprecation to be
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
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
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.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.
forRemoval on Warning Policy
The Java Language Specification, section 18.104.22.168
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
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
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:
use site | API declaration site context | not dep. deprecated +----------------------- not dep. | N W | deprecated | N N (1) N = no warning W = warning
(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 22.214.171.124 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".
The proposed table of cases is shown below:
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)
(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
@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
- Use site X depends on API Y, currently not deprecated
- Y's declaration changes to ordinary deprecation, generating ordinary deprecation warning at X
- X is annotated with
@SuppressWarnings("deprecation"), suppressing the warning
- Y's declaration changes to terminal deprecation; removal warning at X still suppressed
- Y is removed entirely, causing X to break unexpectedly
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
Removal warnings -- warnings that arise from the use of terminally deprecationed APIs -- can be suppressed with the annotation
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
@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
As noted above, this annotation suppresses only ordinary deprecation warnings; it doesn't suppress removal warnings.
If it is necessary to suppress both ordinary deprecation warnings and removal warnings at a particular site, the following construct can be used:
Below is a copy of the warnings table from the previous section, modified to show how warnings from the different cases can be suppressed.
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")
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 126.96.36.199 will need to be modified accordingly. That change is covered by JDK-8145716.
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
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
If true, as determined by the
Boolean.parseBooleanmethod, 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
java.deprecation.enableStackTrace— boolean, default
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.
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
Javadoc tag should be largely unchanged, though perhaps it might be
modified somewhat to include information about the
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.
@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.
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
@Deprecated annotation should always both be present or
absent. However, they are redundant only in very abstract, conceptual
@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
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
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.
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
be run to ensure that it issues warnings for all such usages.