Tuesday, November 3, 2015

What Might a New @Deprecated Look Like?

JDK Enhancement Proposal (JEP) 277 ("Enhanced Deprecation") proposes to "revamp the deprecation annotation and provide tools to strengthen the tail end of the feature life cycle." Some of the limitations of the current @java.lang.Deprecated have bothered me for some time. I particularly would like to be able to provide text with @Deprecated rather than being forced to place explanatory text in a corresponding Javadoc @deprecated comment. In this post, I look at a custom annotation that gives a feel for the type of extra metadata JEP 277 proposes be included in a new and improved @Deprecated annotation.

The code listing that follows contains the definition of dustin.examples.annotations.Deprecated, an implementation which mostly mirrors what is described in the JEP 277 proposal.

dustin.examples.annotations.Deprecated

package dustin.examples.annotations;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.TYPE;

/**
 * Conceptual improvement on standard @java.lang.Deprecated annotation
 * based on preliminary discussion related to JEP 277 and on
 * desire to include context details with deprecation annotation
 * rather than relying on presence of Javadoc's @deprecated.
 *
 * Javadoc comments in this annotation definition are largely
 * based on descriptions in JEP 277 (http://openjdk.java.net/jeps/277).
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({CONSTRUCTOR,FIELD,LOCAL_VARIABLE,METHOD,PACKAGE,PARAMETER,TYPE})
public @interface Deprecated
{
   /**
    * JEP 277-defined reasons and associated explanations.
    */
   public enum Reason
   {
      /**
       * This API has been deprecated without any reason having been given.
       * This is the default value; everything that's deprecated today
       * implicitly has a deprecation reason of UNSPECIFIED.
       */
      UNSPECIFIED,
      /**
       * This API is earmarked for removal in a future JDK release. Note,
       * the use of the word "condemned" here is used in the sense of a
       * structure that is intended to be torn down. The term is not mean
       * to imply any moral censure.
       */
      CONDEMNED,
      /**
       * Use of this API can lead to data loss, deadlock, security
       * vulnerability, incorrect results, or loss of JVM integrity.
       */
      DANGEROUS,
      /**
       * This API is no longer necessary, and usages should be removed.
       * No replacement API exists. Note that OBSOLETE APIs might or
       * might not be marked CONDEMNED.
       */
      OBSOLETE,
      /**
       * This API has been replaced by a newer API, and usages should be
       * migrated away from this API to the newer API. Note that SUPERSEDED
       * APIs might or might not be marked CONDEMNED.
       */
      SUPERSEDED,
      /**
       * Calling this has no effect or will unconditionally throw an exception.
       */
      UNIMPLEMENTED,
      /**
       * This API is not a stable part of the specification, and it may
       * change incompatibly or disappear at any time.
       */
      EXPERIMENTAL;
   }

   /**
    * Provides any combination of one or more of the enum constants,
    * although not all combinations make sense. It is syntactically possible,
    * though perverse, to provide an empty array. Such a case should be
    * treated as if UNSPECIFIED were present.
    *
    * @return One or more Reasons for deprecation; default value is the enum
    *    constant UNSPECIFIED and absence of values should be treated as such.
    */
   Reason[] value();

   /**
    * Provides Strings representing any APIs that replace this API.
    * This should not specify any replacements if reason is OBSOLETE.
    *
    * @return Strings returned by this method should be links to replacement
    *    APIs for the API being deprecated. Each string should be in the same
    *    format as the @link Javadoc tag.
    */
   String[] replacement();

   /**
    * Provides the release in which the API was deprecated.
    *
    * @return Release number at which this API became deprecated
    *    in a free-form syntax String with the release numbering
    *    following the same scheme as the @since Javadoc tag.
    */
   String since();

   /**
    * Provides the anticipated complete removal of this deprecated API
    * if any known date or version is anticipated for the API's removal.
    * This value is most likely to be set for reasons of CONDEMNED,
    * OBSOLETE, and SUPERSEDED. This value is NOT described in JEP 277.
    *
    * @return Date or version in which it is anticipated that this
    *    API will be removed altogether.
    */
   String anticipatedRemoval() default "Not Planned";
}

The next code listing provides examples of the above annotation being applied to a deprecated class.

DeprecatedClass.java: Examples of Using Improved @Deprecated

package dustin.examples.annotations.demo;

import java.text.DateFormat;
import java.text.SimpleDateFormat;

import static dustin.examples.annotations.Deprecated.Reason.*;

/**
 * Demonstrates how new and improved @Deprecated might be used.
 */
@dustin.examples.annotations.Deprecated(
   value={SUPERSEDED, CONDEMNED},
   since="1.5", anticipatedRemoval="1.9",
   replacement="dustin.examples.annotations.demo.OtherClass")
public class DeprecatedClass
{
   final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");

   @dustin.examples.annotations.Deprecated(
      value={DANGEROUS}, since="1.0",
      replacement="java.text.SimpleDateFormat#SimpleDateFormat")
   public DateFormat getDateFormatter()
   {
      return dateFormat;
   }
}

This last example demonstrates use of the improved @Deprecated annotation. The "anticipatedRemoval" element of the @Deprecated annotation is not mentioned in JEP 277 and I gave it a default value for situations in which a deprecated construct may have no anticipated removal date but rather is deprecated to warn off new uses of it only.

The code listings above demonstrate defining a new and improved @Deprecated annotation such as spelled out in JEP 277. However, JEP 277 proposes much more than the existence of an improved annotation. The proposal also discusses "runtime changes" to provide "warnings about usage of deprecated APIs on an opt-in basis," "dependency-tool enhancements" to analyze annotation dependencies in fashion similar to and possibly even based on the jdeps tool, and "Javadoc enhancements."

Although Java's custom annotations support made it easy to implement a version of @Deprecated that reflects many of the ideas in JEP 277, the new and improved @java.lang.Deprecated would enjoy many benefits that a custom Java annotation does not enjoy such as built-in support in the JDK and use by JDK classes. The JDK-provided @Deprecated would also continue to enjoy IDE and tooling benefits such as striked-out names of deprecated code constructs.

3 comments:

Sergey Ponomarev said...

Hi Dustin,

I would like to use your JEP277-like annotation right now in project on my work.
I know that this is still just a proposal but we need this functionality today.
Could you publish your annotation on GitHub with some business friendly license like Apache 2?

Thanks

@DustinMarx said...

JDK-8065614 has been written to cover "JEP 277: Enhanced Deprecation."

@DustinMarx said...

Plans for JDK 9 enhancements to @Deprecated have changed significantly since this post and are described in more detail in the post JDK 9 @Deprecated Annotation Enhancements.