I have occasionally run into a situation in which I have needed to introduce a new API or construct for others to try out, but have known that it might change based on others' feedback after some use of it. In such cases, I've wanted to somehow annotate the construct to warn other developers of the tentativeness of this newly added construct. There are several alternatives that I have considered in these cases.
- Use a third-party annotation such as Guava's @Beta annotation.
- Develop a custom annotation.
- Use comments/Javadoc only.
- Use @Deprecated annotation with Javadoc @deprecated tag.
Third-party Annotation
The Javadoc documentation for Guava's @Beta
annotation states:
Signifies that a public API (public class, method or field) is subject to incompatible changes, or even removal, in a future release. An API bearing this annotation is exempt from any compatibility guarantees made by its containing library. Note that the presence of this annotation implies nothing about the quality or performance of the API in question, only the fact that it is not "API-frozen."
This explanation of the use of @Beta
seems to imply this is a good fit for a "new" construct that may be removed. I talked more about this annotation in the blog post "Two Generally Useful Guava Annotations".
Other considerations when using a third-party library's annotation is that the third-party library must be included on one's classpath and that there is typically no out-of-the-box support in the most popular Java IDEs to indicate special treatment of the construct annotated with the annotation.
Custom Annotation
If one is not using the library with the annotation for any other reason, it can seem a bit heavy to add a new library dependency simply for an annotation when it's relatively straightforward to write one's own custom annotation. I have written about writing a custom @Unfinished annotation before and that post discussed how to create corresponding custom IDE inspections in NetBeans 8.0.2 and IntelliJ IDEA 14.0.3 for this custom annotation.
The following code listing provides an example of a custom annotation one could use for this purpose.
@Preview Annotation
package dustin.examples.annotations; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Preview { /** * Anticipated release in which Preview status will no longer apply. * * @return Anticipated release of feature */ String transition() default ""; /** * Version in which this preview feature was introduced. * * @return Release in which this preview feature was introduced. */ String since() default ""; /** * Reasons this construct is considered "preview." * * @return Reasons this construct is considered preview. */ String[] reasons() default {}; }
The custom annotation lacks any out-of-the-box support in the popular Java IDEs.
Comments Only
Annotations don't necessarily need to be used and simple comments (Javadoc or otherwise) could explain that a particular construct is preliminary and might go away. However, comments are weaker in many ways than annotations in terms of communicating intent. It is much more difficult to have an IDE or other tooling parse comments than to process annotations.
@Deprecated Annotation and @deprecated Javadoc Tag
One can use @Deprecated
to annotate a deprecated construct with a standard annotation that IDEs, tools, and scripts can easily process. Unfortunately, the @Deprecated
annotation never got the full support I had hoped it would get for more specifically specifying why something was deprecated when it was decided to make the JDK 9 enhanced @Deprecated much less ambitious. The Javadoc @deprecated
tag can be used to document that the deprecation is actually for a "new" construct that might go away, but also might not go away. The @Deprecated
annotation and the @deprecated
Javadoc tag can be removed if it's decided to keep the "preview" construct.
Although the @Deprecated
annotation and @deprecated
Javadoc tag enjoy benefits from being standards that include built-in IDE support and awareness among most Java developers, it can still feel a bit inappropriate to use these to mark a new construct that may go away, but may stick around. The "When to Deprecate" section of the document "How and When To Deprecate APIs" states, "When you design an API, carefully consider whether it supersedes an old API." It further lists three reasons for deprecation which are "insecure, buggy, or highly inefficient", "going away in a future release", and "encourages bad coding practices".
I'm not the only one who thinks of "deprecation" as marking something likely to be removed or that should not be used. Nicolas Fränkel outlines the feature lifecycle in Java and explains that deprecation in Java is "a bold and clear statement to everyone that a feature version has no future, at least in its current form."
In the jdk-dev mailing list message "JEP 12: Treatment of standard APIs supporting preview features," Alex Buckley writes:
We'd like to use deprecation-for-removal-at-birth as the way to flag "this API is intimately connected to a preview feature". If the preview feature becomes permanent, then the deprecation would be removed. This jump from terminal-deprecation to no-deprecation is novel, but not mad -- deprecation has a variety of meanings, and its historical use in the JDK is not a good guide to anything.
Buckley also cites a paragraph from JEP 277 ("Enhanced Deprecation") regarding use of the deprecation mechanisms (I have highlighted the same portion Buckley emphasized):
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.
The JDK 9-introduced "enhanced" @Deprecated
annotation can help a bit in this situation (that Buckley termed "deprecation-for-removal-at-birth") via its newly added "since" and "forRemoval" elements. Specifying the @Deprecated
annotation's forRemoval()
as false
and specifying its since
as the same version as the Javadoc @since tag might help developers to see that the construct was deprecated from the beginning with no current plans to remove it. For such an approach to be most effective, it'd probably be write to explicitly state forRemoval
as false rather than relying on its implicit default.
It may be that we Java developers will need to start thinking of @Deprecated
and @deprecated
a bit differently than in the past. Although the @Deprecated
annotation and @deprecated
Javadoc tag still "inform" us of "the risks of continuing dependency" upon the annotated/described construct, it may be incorrect to assume that such a construct is necessarily going away at some point in the future. If we get used to this alternate meaning in deprecated JDK constructs, we'll be more likely to consider using the same approach with our own newly added and still tentative features.
No comments:
Post a Comment