Maintaining APIs is hard.

We’re maintaining the jOOQ API which is extremely complex. But we are following relatively relaxed rules as far as semantic versioning is concerned.

When you read comments by Brian Goetz and others about maintaining backwards-compatibility in the JDK, I can but show a lot of respect for their work. Obviously, we all wish that things like Vector , Stack , Hashtable were finally removed, but there are backwards-compatibility related edge cases around the collections API that ordinary mortals will never think of. For instance: Why aren’t Java Collections remove methods generic?

Better Deprecation

With Java 9, Jigsaw, and modularity, one of the main driving goals for the new features is to be able to “cut off” parts of the JDK and gently deprecate and remove them over the next releases. And as a part of this improvement, Stuart Marks AKA Dr Deprecator has suggested JEP 277: “Enhanced Deprecation”

The idea is for this to enhance the @Deprecated annotation with some additional info, such as:

UNSPECIFIED . 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.

. 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. CONDEMNED . 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.

. 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. DANGEROUS . Use of this API can lead to data loss, deadlock, security vulnerability, incorrect results, or loss of JVM integrity.

. Use of this API can lead to data loss, deadlock, security vulnerability, incorrect results, or loss of JVM integrity. OBSOLETE . 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.

. 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. SUPERSEDED . 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.

. 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. UNIMPLEMENTED . Calling this has no effect or will unconditionally throw an exception.

. Calling this has no effect or will unconditionally throw an exception. EXPERIMENTAL. This API is not a stable part of the specification, and it may change incompatibly or disappear at any time.

When deprecating stuff, it’s important to be able to communicate the intent of the deprecation. This can be achieved as well via the @deprecated Javadoc tag, where any sort of text can be generated.

An alternative, much better solution

The above proposition suffers from the following problems:

It’s not extensible . The above may be enough for JDK library designers, but we as third party API providers will want to have many more elements in the enum, other than CONDEMNED, DANGEROUS, etc.

. The above may be enough for JDK library designers, but we as third party API providers will want to have many more elements in the enum, other than CONDEMNED, DANGEROUS, etc. Still no plain text info . There is still redundancy between this annotation and the Javadoc tag as we can still not formally provide any text to the annotation that clarifies, e.g. the motivation of why something is “DANGEROUS”.

. There is still redundancy between this annotation and the Javadoc tag as we can still not formally provide any text to the annotation that clarifies, e.g. the motivation of why something is “DANGEROUS”. “Deprecated” is wrong. The idea of marking something UNIMPLEMENTED or EXPERIMENTAL as “deprecated” shows the workaround-y nature of this JEP, which tries to shoehorn some new functionality into existing names.

I have a feeling that the JEP is just too afraid to touch too many parts. Yet, there would be an extremely simple alternative that is much much better for everyone:

public @interface Warning { String name() default "warning"; String description() default ""; }

There’s no need to constrain the number of possible warning types to a limited list of constants. Instead, we can have a @Warning annotation that takes any string!

Of course, the JDK could have a set of well-known string values, such as:

public interface ResultSet { @Deprecated @Warning(name="OBSOLETE") InputStream getUnicodeStream(int columnIndex); }

or…

public interface Collection<E> { @Warning(name="OPTIONAL") boolean remove(Object o); }

Notice that while JDBC’s ResultSet.getUnicodeStream() is really deprecated in the sense of being “OBSOLETE”, we could also add a hint to the Collection.remove() method, which applies only to the Collection type, not to many of its subtypes.

Now, the interesting thing with such an approach is that we could also enhance the useful @SuppressWarnings annotation, because sometimes, we simply KnowWhatWeAreDoing™, e.g. when writing things like:

Collection<Integer> collection = new ArrayList<>(); // Compiler!! Stop bitching @SuppressWarnings("OPTIONAL") boolean ok = collection.remove(1);

This approach would solve many problems in one go:

The JDK maintainers have what they want. Nice tooling for gently deprecating JDK stuff

The not-so-well documented mess around what’s possible to do with @SuppressWarnings would finally be a bit more clean and formal

would finally be a bit more clean and formal We could emit tons of custom warnings to our users, depending on a variety of use-cases

Users could mute warnings on a very fine-grained level

For instance: A motivation for jOOQ would be to disambiguate the DSL equal() method from the unfortunate Object.equals() method:

public interface Field<T> { /** * <code>this = value</code>. */ Condition equal(T value); /** * <strong>Watch out! This is * {@link Object#equals(Object)}, * not a jOOQ DSL feature!</strong> */ @Override @Warning( name = "ACCIDENTAL_EQUALS", description = "Did you mean Field.equal?" ) boolean equals(Object other); }

The background of this use-case is described here:

https://github.com/jOOQ/jOOQ/issues/4763

Conclusion

JEP 277 is useful, no doubt. But it is also very limited in scope (probably not to further delay Jigsaw?) Yet, I wish this topic of generating these kinds of compiler warnings would be dealt with more thoroughly by the JDK maintainers. This is a great opportunity to DoTheRightThing™

I don’t think the above “spec” is complete. It’s just a rough idea. But I had wished for such a mechanism many many times as an API designer. To be able to give users a hint about potential API misuse, which they can mute either via:

@SuppressWarnings , directly in the code.

, directly in the code. Easy to implement IDE settings. It would be really simple for Eclipse, NetBeans, and IntelliJ to implement custom warning handling for these things.

Once we do have a @Warning annotation, we can perhaps, finally deprecate the not so useful @Deprecated …

@Warning(name = "OBSOLETE") public @interface Deprecated { }

Discussions

See also follow-up discussions on: