Alternative suggestion for JEP 277 "Enhanced Deprecation"

Stuart Marks stuart.marks at oracle.com
Thu Jan 7 23:00:14 UTC 2016


Hi Lukas,

Thanks for your interest in deprecation!

I definitely wrote JEP with the JDK's usage primarily in mind, but it shouldn't 
have anything that precludes usage by other libraries. Some of the examples I 
used might have led people to think it was JDK-only. It isn't, though its 
primary consumer will be the JDK itself.

There's a lot in your proposal, so I'll respond only to a couple key points.

One prominent issue is whether the namespace of "reason" values should be 
strings or enums. The main reasons for using enums are strong typing and clear 
semantics.

It's interesting that you mention @SuppressWarnings, because the use of a string 
value here is widely viewed as a mistake (at least by the JDK folks I talked to, 
and me). The reason is that different toolsets have different and overlapping 
vocabularies, and where they overlap, it's unclear whether they are treated 
consistently.

In addition, having an uncontrolled namespace means that, in practice, every 
tool has to ignore string values it doesn't understand. So if Oracle's javac sees

     @SuppressWarnings("hiding")

it pretty much has to assume that the value is intended for processing by some 
other tool. However, it also means that misspelled tags cannot be checked at 
compile time, code completion doesn't work, etc. If @Deprecated were to use 
strings, I can easily imagine seeing the following:

     @Deprecated("SUPERCEDED")

(For the sake of example, this is a deliberate misspelling of "SUPERSEDED".) 
Some tools will start to accept both spellings, and others won't, leading to 
confusion.

As you noted, the documentation of @SuppressWarnings strings is lacking. Better 
documentation will help, but the fundamental problem is that the 
@SuppressWarnings namespace is uncontrolled and is weakly typed (or "stringly 
typed" if you will). I don't want to repeat that mistake with @Deprecated.

What's lost by using enums instead of strings is extensibility by third party 
libraries. Is this really useful, though? How many different reasons are there 
to deprecate something? Now, the list in the JEP is reasonably complete, but 
it's always possible that something is missing. Things can definitely be added 
(and indeed there is still time to do so in JDK 9). So please offer suggestions.

One possibility that's come up is to add an OTHER reason, which is a catch-all 
to use when the other reasons don't fit. The real reason will need to be 
explained in the javadoc text anyway. (It differs from UNSPECIFIED, which is the 
default value in case no reason is specified. Maybe UNSPECIFIED is better named 
as UNKNOWN.)

**

One point that you and a couple others have mentioned is that certain things 
like EXPERIMENTAL and UNIMPLEMENTED don't fit. I think they do, but it requires 
some explanation. (At some point I'll update the JEP to clarify this.)

Deprecation is about API evolution and communicating its impact on clients of 
that API, to inform clients of the need to perform code migration.

If you're an application and you use an API marked EXPERIMENTAL, you ought to be 
notified of that. The reason is that in a future release, such APIs are likely 
to disappear, or change incompatibly, or be renamed, e.g., from 
jdk.experimental.foo to java.foo. All of these cases are binary incompatible and 
are thus code migration issues.

An example of UNIMPLEMENTED is Thread.stop(Throwable). In JDK 7, this actually 
worked, but of course it was dangerous. In JDK 8 this has been changed to throw 
UnsupportedOperationException unconditionally. This is clearly a behavioral 
incompatibility. (See Joe Darcy's blog [1] for discussion of the different kinds 
of compatibility.) Anyway, code that relies on this method clearly needs to 
migrate away from it.

[1] https://blogs.oracle.com/darcy/entry/kinds_of_compatibility

**

Now the idea of @Warning is kind of interesting, as are your examples of 
"OPTIONAL" and "ACCIDENTAL_EQUALS". Speaking of equals(), an example in the JDK 
might be using equals() to compare two PriorityQueue instances; this doesn't do 
what one might think it would do!

But these are more about API usage that has potential pitfalls, and less about 
code migration. For the "OPTIONAL" case (Collection.remove) the programmer has 
to make sure that the collection instance is actually mutable, and if it is, 
there's no need for code migration.

The other cases are more interesting. It seems like it's almost always an error 
to call equals() on a jOOQ Field; it also seems a likely error to call equals() 
on PriorityQueues.

Guava has similar cases, where calling a mutation method on an 
ImmutableCollection is certainly an error. [2] In fact, these methods are 
@Deprecated in order to generate compile-time warnings.

[2] 
http://google.github.io/guava/releases/snapshot/api/docs/com/google/common/collect/ImmutableCollection.html#remove%28java.lang.Object%29

But none of these are about API evolution, so deprecation doesn't seem to apply 
here. (Guava's usage of deprecation does seem like a misuse, though I have to 
admit it's effective.)

Your point about @Warning is that it "gives users a hint about potential API 
misuse." I think that's a fine thing to do, and maybe there are other cases 
where it's compelling. However, this is different from API evolution and code 
migration, so @Warning won't be a replacement for @Deprecated. If you want to 
pursue a @Warning proposal, though, be my guest; I don't see any problem with a 
potential future @Warning coexisting with @Deprecated.

s'marks



On 12/29/15 6:08 AM, Lukas Eder wrote:
> Hello,
>
> I've recently discovered JEP 277, which is a very good move forward for the
> JDK, but in my opinion not really optimal for the Java ecosystem as a whole
> - as it responds only to a limited set of requirements that are mostly
> beneficial to the development of JDK 9, I assume.
>
> I have suggested an alternative approach in this blog post here:
> http://blog.jooq.org/2015/12/22/jep-277-enhanced-deprecation-is-nice-but-heres-a-much-better-alternative
>
> It essentially consists of adding a more generic @Warning annotation,
> roughly like this:
>
>      public @interface Warning {
>          String name() default "warning";
>          String description() default "";
>      }
>
> Which could then be used on JDBC:
>
>      public interface ResultSet {
>
>          @Deprecated
>          @Warning(name="OBSOLETE")
>          InputStream getUnicodeStream(int columnIndex);
>      }
>
> Or on the Collections APIs:
>
>      public interface Collection<E> {
>
>          @Warning(name="OPTIONAL")
>          boolean remove(Object o);
>      }
>
> Or wherever it may seem suitable. The above are just examples.
>
> The advantage of a generic, String-based approach is that @SuppressWarnings
> could be retrofitted to accept any possible @Warning(name):
>
>      Collection<Integer> collection = new ArrayList<>();
>      @SuppressWarnings("OPTIONAL")
>      boolean ok = collection.remove(1);
>
> The same is true for IDEs, which could filter out any such warnings based
> on user settings.
>
> While still addressing the goals mentioned in JEP 277, this would add much
> more substantial value to the whole ecosystem, and to all other library /
> API designers as well.
>
> Curious to hear your feedback,
> Lukas Eder
>


More information about the jdk9-dev mailing list