Preview, incubator, removed
Structured concurrency is still preview on Java 25, and the Java 21 code does not compile there. That is what "may change" means.
Java ships every six months, and not everything in a release is finished. Four words decide whether you may build on something, and getting them wrong is how a service ends up pinned to a JDK nobody wants to maintain.
The four states
| State | What it means | May you use it in production? |
|---|---|---|
| GA | final, and covered by the compatibility promise | yes |
| Preview | complete but not final; may change or disappear | no |
| Incubator | an API not yet even a preview; a separate module | no |
| Deprecated | still works, marked for removal | yes, and plan to stop |
| Removed | gone | it will not compile |
A preview feature is off by default, and turning it on is deliberately awkward. You need --enable-preview at compile time and at run time, and the class files are stamped with the JDK version that produced them — so a class compiled with preview on Java 21 will not run on Java 22 at all, even with preview enabled. That is not an oversight; it is the mechanism that stops preview code spreading.
What "may change" actually means
Structured concurrency arrived as a preview in Java 21. Here is that Java 21 code, on Java 25:
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
scope.fork(() -> 1);
scope.join();
}Prev.java:4: error: StructuredTaskScope is a preview API and is disabled by default.
(use --enable-preview to enable preview APIs)So it is still preview on Java 25 — four releases and an LTS later. And with --enable-preview turned on:
Prev.java:4: error: cannot find symbol
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
^
symbol: class ShutdownOnFailure
location: interface StructuredTaskScopeRead the last line. In Java 21 StructuredTaskScope was a class with a nested ShutdownOnFailure. In Java 25 it is an interface, and that nested class is gone. The code does not compile even with preview enabled.
That is the whole lesson in one error message: preview means the API can change shape, and it did. A team that built on it in 21 has a rewrite, not an upgrade.
Where preview is worth using: a spike, a benchmark, a prototype you will throw away, and feedback — which is the point of the mechanism. The JDK team ships previews to find out what is wrong with them, and that only works if people try them somewhere disposable.
Incubator is one step earlier
An incubator module is an API that is not yet ready to be a preview. It lives in a module named jdk.incubator.something, and using it needs --add-modules.
The vector API has been in incubation since Java 16 and is still there. That is the honest signal about timelines: incubator means years, not releases.
Deprecated, and the two kinds
Java deprecates far more than it removes, and the annotation now carries the distinction:
@Deprecated(since = "9", forRemoval = true)forRemoval = falseis advisory.Date's constructors have been deprecated since 1997 and still work. You should move; nothing is forcing you.forRemoval = trueis a countdown.Thread.stopwent this way and is now gone; the finalization mechanism is on the same path.
The practical habit: compile with -Xlint:deprecation and read the output. A forRemoval = true warning is a future build failure with a date nobody has told you.
What has actually been removed
Removals are rare and they cluster around a few upgrades, which is why the upgrading lesson treats them as the main event:
- Java 11 removed the Java EE and CORBA modules — JAXB, JAX-WS,
javax.annotation. This is the most common Java 8 → 11 break, and the fix is adding the artifacts as ordinary dependencies, because they now live on Maven Central rather than in the JDK. - Java 17 removed the Security Manager's usability and the experimental AOT compiler.
- Java 21 removed the 32-bit Windows port.
Almost everything else is deprecation without removal, which is why Java's compatibility reputation is deserved even though the release cadence sounds alarming.
Reading a JEP
Every feature arrives as a JEP with a status line, and it is the fastest way to answer "can I use this":
- Status: Draft, Candidate, Integrated, Closed/Delivered.
- Release: which version it landed in.
- Whether the delivery says preview, second preview, incubator or nothing.
"Second preview" is a useful signal in both directions: the feature is being taken seriously, and it changed since the first one.