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