Preview APIs in the Java Platform

Alex Buckley alex.buckley at
Tue Mar 3 21:15:31 UTC 2020

Java 14 will be the third release to contain preview language features. 
The idea of shipping non-final language features -- conceived by JEP 12 
in 2018 -- is turning out well, producing better final features. This 
made us wonder if incubation -- conceived by JEP 11 in 2016 -- is the 
right channel for shipping non-final APIs, and if the recent 
introduction of APIs associated with preview language features (such as 
`java.lang.Record`) is a signpost to a better channel.

Incubation follows the tenor of the old triennial release model, where 
features were chosen at the start of a release and their evolving 
implementations were shipped in the JDK's Early Access (EA) binaries for 
years before General Availability (GA). To signal that an API is 
non-final both before and after GA, incubation places it in the 
`jdk.incubator` namespace. Unfortunately, this distorts the API and its 
implementation [1][2], and means that signatures in `java.*` cannot 
refer to the new API even if such integration is desirable. These 
problems are not significant for user-level libraries such as the HTTP2 
client API which incubated in JDK 9, but they are significant for lower 
level libraries which need a privileged relationship with `java.base`, 
such as the Memory Access API which incubated in JDK 14.


In the new biannual release model, features are targeted to a release 
only when they are ready. Until then, they evolve in OpenJDK projects 
such as Panama and Valhalla, watching JDK releases sail by every six 
months. There is broad public awareness of these projects, and they 
generally offer EA binaries, so there is good potential for feedback in 
the time before a feature is targeted to a release. Also, because 
OpenJDK projects are blueprints for the future Java Platform, they can 
place non-final APIs directly in `java.base` and refer to them from 
signatures in `java.*`. This makes projects' EA binaries look more 
polished and should produce higher quality feedback.

Ultimately, though, the best way to provoke feedback on a feature is to 
ship it in the GA binary of a JDK feature release. This approach has 
worked well for preview language features, where the Java community has 
accepted the idea of non-final features that are disabled by default and 
can thus be changed in response to feedback. Ideally, we want a way to 
ship highly-evolved but non-final APIs in a JDK feature release, without 
distorting the API by relocating its packages and modules, and without 
misleading developers about its status.

Most "preview principles" carry over from language features to APIs:

1. A _preview API_ is a new method, field, class, package, or module in 
the Java Platform whose design, specification, and implementation are 
semantically complete, but which would benefit from a period of broad 
exposure and evaluation before achieving either final and permanent 
status in the Java Platform or else being refined or removed.

We would recast the quality bar for all preview features from "95% done 
now" to "100% done within a year". This recognizes two points: first, 
our experience that two rounds of preview is normal, and second, the 
fact that an API has a larger surface area than a language/VM feature 
and thus undergoes more syntactic polishing on its way to final status.

2. A preview API will often reside in the `java.base` module, but may 
reside in another `java.*` module, including one introduced just for the 
preview API. For example, the HTTP2 client API could have previewed in 
the `` module, where it ended up after incubation.

A JEP that introduces many packages may designate them all as preview 
APIs and place them in different `java.*` modules as it sees fit.

3. Preview APIs are unavailable by default. To use them, a developer 
"opts in" in the same way as for preview language features: `--release N 
--enable-preview` at compile time. The class files of the developer's 
program are marked to depend on the preview APIs of Java version N, as 
if the program had used preview language features. Accordingly, the 
class files must be executed with `--enable-preview` at run time, and 
only the same JDK version.

Java 14 already has "APIs associated with preview language features" 
that work this way, such as `java.lang.Record`. In future, such APIs 
would simply be cast as preview APIs. The existing private mechanism 
that identifies them to `javac` and `javadoc` -- 
`@jdk.internal.PreviewFeature` -- will be used for all preview APIs.

4. The class files of a preview API itself are _not_ marked. There are 
no changes to how the JDK is compiled, and every class file in the JDK 
will have a 0 minor_version as before.

To allow for intra-JDK use of a preview API, code in the same module as 
a preview API is _not_ required to "opt in" in order to use the API. 
That is, when `--enable-preview` is missing, the effect of using a 
preview API element is a compile-time error _only for code in other 
modules_. This is similar to how the effect of using an `@Deprecated` 
element is a warning _only for code that is not itself deprecated_.

Beyond APIs, incubation has been used for tools, e.g., 
`jdk.incubator.jpackage` in JDK 14. However, it has little real meaning 
there. A tool that's good enough to ship in a JDK feature release has 
already achieved a high level of quality and is ready for a final round 
of polishing for its command line options. As long as the tool displays 
a suitable message about its non-final status, it can legitimately be 
called a "preview tool" and placed in a module in the ordinary `jdk` 
namespace rather than the `jdk.incubator` namespace.

We don't propose to deprecate incubation or delete JEP 11. It may be 
useful in future for non-final APIs that wish to live at arms' length 
from the JDK, outside the `java` namespace.

I intend to update JEP 12 to incorporate preview APIs in the near 
future, hopefully in time for 15 so that projects such as Panama can 
benefit from them.


More information about the jdk-dev mailing list