preview and incubator features
--enable-preview, and how features graduate.
- chapter
- chapter 08.02
- reading time
- 6 min read
a preview feature is finished but not permanent: fully specified and implemented, shipped so people can try it, and allowed to change or disappear. you opt in twice, with --enable-preview on javac and on java.
an incubator is an earlier, rougher API in a jdk.incubator.* module. an experimental feature is a JVM option behind -XX:+UnlockExperimentalVMOptions. all three are how big things reach java without being locked in forever on day one.
the picture
you know how a java feature, once it's in, is in forever? Vector, Date, Thread.stop and the rest of the museum are proof. java is serious about backward compatibility, which makes every design mistake permanent. previews are the escape hatch: ship it for real, let millions of people kick the tires, then decide.
think of a restaurant trying a new dish as a "special" before it goes on the printed menu. it's a real dish, cooked properly, you can order it. but next month it might come back with a different sauce, or not at all. you have to ask for it by name (that's --enable-preview).
Note: Main.java uses preview features of Java SE 25. Note: Recompile with -Xlint:preview for details.
java.lang.UnsupportedClassVersionError: Preview features are not enabled for Main (class file version 69.65535). Try running with '--enable-preview'
javac stamped the class file with minor version 0xFFFF, which means 'depends on preview features'. the JVM checks that stamp before running a single line.
the rules of the game
the process is written down in JEP 12. the parts worth knowing:
- what can preview
- language features (
instanceof byte b), JVM features, andjava.*APIs (StableValuein 25). an API preview fails to compile without the flag too:error: StableValue is a preview API and is disabled by default. - how long
- the target is final within about a year: preview in release N and N+1, final in N+2. big ones take longer. structured concurrency has been previewing since 21.
- what happens after
- it goes final (maybe with changes), previews again (that needs a new JEP), or gets removed. string templates were removed.
- the stamp
- a class file that uses preview features gets minor version 65535 (all 16 bits set): 69.65535 for java 25. it runs only on that exact release, and only with
--enable-preview. - the noise
- with the flag on, javac reminds you on every compile (
Note: Main.java uses preview features of Java SE 25.). that's on purpose.
$ javac --release 25 --enable-preview Main.java
Note: Main.java uses preview features of Java SE 25.
Note: Recompile with -Xlint:preview for details.
$ javap -v Main.class | grep version
minor version: 65535
major version: 69
$ java --enable-preview Main
300 doesn't fit in a byte
$ java --enable-preview --source 25 Main.java # the source launcher, both in one gomaven and gradle need the flag in two places too: the compiler plugin (<compilerArgs>--enable-preview</compilerArgs>) and whatever runs the code, tests included (surefire's argLine, gradle's jvmArgs). forgetting the second one is the classic "it compiles but every test fails" afternoon.
incubator and experimental
previews are for things that are almost done. there are two earlier stages:
$ java --add-modules jdk.incubator.vector Main.java
WARNING: Using incubator modules: jdk.incubator.vectorthe API lives in jdk.incubator.vector, not java.*, so the package name itself tells you it's temporary. when it graduates, it moves and your imports change.
$ java -XX:+UseEpsilonGC -version
Error: VM option 'UseEpsilonGC' is experimental and must be enabled via -XX:+UnlockExperimentalVMOptions.this is how runtime features start: a new GC mode, a new header layout. no API, just a switch.
| stage | for | opt in with | can change? | example |
|---|---|---|---|---|
| incubator (JEP 11) | APIs, early | --add-modules jdk.incubator.x | completely | vector API, since 16 |
| experimental | JVM features | -XX:+UnlockExperimentalVMOptions | yes | compact object headers in 24 |
| preview (JEP 12) | language, VM, APIs, nearly done | --enable-preview | yes, or be removed | primitive patterns in 27 |
| final | everything | nothing | no | records, since 16 |
see every feature's path through these stages in the release timeline, and what's in preview right now in what's in preview.
- preview = finished but not permanent. it can change, re-preview, or be removed.
--enable-previewon javac and on java. the source launcher also needs--source N.- preview class files are stamped N.65535 and only run on that exact release.
- incubator =
jdk.incubator.*module +--add-modules. experimental =-XX:+UnlockExperimentalVMOptions. - great for trying things. a liability in production.