Executive summary
I read the announcement that JEP 401 will bring value classes to JDK 28 as a preview feature. The matter for engineers here is the compatibility risk: enabling previews changes the behavior of 30 JDK classes even for code compiled earlier, so projects should test both disabled and enabled preview modes.
The problem being solved
Quality Outreach OpenJDK wants FOSS projects to help test JDK 28 builds to improve release quality. The program includes testing with preview features disabled—the default JDK—and with preview features enabled, because the behavioral differences are substantial and not yet fully covered by routine testing.
This change matters because projects that do not modify their source code may still experience semantic differences. JDK 28 should behave as-is when previews are disabled, but the depth of the changes still creates a possibility of unintended regressions even in that mode.
Key features and code examples
When preview is enabled, 30 JDK classes become value classes. The list includes primitive wrappers such as Integer, Long, Double, and Boolean; Optional and its primitive variants; many java.time classes; and the date classes in java.time.chrono.
A value class has no object identity. == compares field values, so two instances with the same values may be considered identical:
var x = LocalDate.of(2026, 7, 31);
var y = LocalDate.of(2026, 7, 31);
var identical = x == y;
// false without value classes
// true with value classesThe consequences for identity-sensitive operations also change:
==may producetruein conditions that previously producedfalse.- A value object has no object monitor. Synchronization on such an object becomes a compile error or triggers an
IdentityException;waitandnotifyare also unsupported. - The
java.lang.refAPIs andjava.util.WeakHashMapcannot be used with value objects and will throw anIdentityException. - A value object is genuinely immutable. Calling
setAccessibleon its fields results in anIllegalAccessException, including when final field mutation is enabled.
The record constructor changes apply when preview is enabled during compilation with JDK 28. A record constructor runs on early construction by default, which restricts instance access and interaction with this; calling an instance method inside the constructor may become a new compile error. A compact constructor that does so needs to be changed to a non-compact constructor, have its statements removed, or be moved after super()/this(...) completes.
The following example is only an illustration to show the cases that need to be inspected:
record Point(int x, int y) {
Point {
validate(x, y); // potentially invalid during early construction
}
static void validate(int x, int y) {
// validation logic
}
}Impact and upgrade notes
I see three categories of issues that need to be separated during testing: expected behavioral differences between regular classes and value classes, record constructor behavior changes, and unexpected JDK misbehavior reported to the valhalla-dev mailing list.
Projects should build and test their codebase against JDK 28 in two configurations: preview features disabled and enabled. If preview features are enabled, source code that was previously compiled without preview or on a version before 28 can still use JDK value classes and experience those behavioral changes.
Compatibility tests around identity, synchronization, reflection, WeakHashMap, and record constructors need to be updated. JDK 8 actually introduced the concept of value-based classes, and JDK 16 expanded its scope to primitive wrappers; the JEP 401 changes clarify and extend the already known value class restrictions.
For unexpected issues, feedback can be sent to valhalla-dev with registration. Additional details are available in JEP 401.
---
First announced on Inside Java. Read the original source for full details.