JEP 390: Warnings for Value-Based Classes

OwnerDan Smith
TypeFeature
ScopeSE
StatusCandidate
Discussionvalhalla dash dev at openjdk dot java dot net
EffortS
DurationXS
Reviewed byBrian Goetz
Endorsed byBrian Goetz
Created2020/07/08 21:39
Updated2020/10/20 21:11
Issue8249100

Summary

Deprecate the constructors of the primitive wrapper classes for removal and designate these wrapper classes as value-based classes. Define a @ValueBased annotation, apply it to classes in the Java Platform API that claim to be value-based, and provide warnings about improper attempts to synchronize on instances of such classes.

Motivation

The Valhalla Project is pursuing a significant enhancement to the Java programming model in the form of primitive classes. Such classes declare their instances to be identity-free and capable of inline or flattened representations, where instances can be copied freely between memory locations and encoded using solely the values of the instances' fields.

The design and implementation of primitive classes is sufficiently mature that we can confidently anticipate migrating certain classes in the Java Platform to become primitive classes in a future release. Specifically, a candidate primitive class has the following properties:

The Java Platform API uses the term value-based class to informally describe certain classes that satisfy similar constraints. If a class is described as value-based then it will probably be able to become a primitive class.

The primitive wrapper classes (java.lang.Integer, java.lang.Double, etc.) satisfy most of these constraints, but have public constructors that clients can invoke to guarantee a unique identity (e.g., for Integers used as locks). These public constructors have been deprecated since Java 9. If we now deprecate them for removal then the primitive wrapper classes can safely be considered value-based classes.

When a value-based class becomes a primitive class, its instances will be == based on their fields' values, and attempts to synchronize will throw an exception.

These changes may be inconvenient for some, but the workarounds are straightforward: If you need an identity, use a different class—often one you define yourself, but Object or AtomicReference may also be suitable. The benefits of migrating to primitive classes—better performance, reliable equality semantics, unifying primitives and classes—will be well worth the inconvenience.

It will be helpful to warn developers of these risks several releases before the migration occurs. We therefore propose to introduce a JDK-internal annotation that marks value-based classes in the Java Platform API and provide warnings to developers who synchronize on instances of such classes.

Description

The description of value-based class will be updated to align with the above properties. It will communicate to developers that they should exercise caution when using == or identityHashCode, and should not perform synchronization.

The primitive wrapper classes in java.lang (Byte, Short, Integer, Long, Float, Double, Boolean, and Character) will have their constructors deprecated for removal, and will be specified as value-based classes. (These constructors were originally deprecated in Java 9.)

Within the JDK, the jdk.internal.ValueBased annotation interface will be introduced to indicate a class or interface for which no instances are expected to rely on object identity.

The @ValueBased annotation may be applied to the following declarations in the Java Platform API and the JDK:

The following warnings will alert developers using these APIs that synchronization may fail in a future release:

The monitorexit bytecode and the Object methods wait, notify, and notifyAll will thrown an IllegalMonitorStateException if invoked outside of a synchronized statement or method. There is thus no need for warnings about these operations.

For example:

import jdk.internal.ValueBased;

/**
 * This is a
 * <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>
 * class.
 */
@ValueBased
final class Distance {
    final double meters;
    private Distance(double meters) { this.meters = meters; }
    public Distance ofMeters(double m) { return new Distance(m); }
}

Distance d = Distance.ofMeters(20.0);
synchronized (d) { ... } // javac warning & HotSpot warning
Object o = d;
synchronized (o) { ... } // HotSpot warning

When a value-based class becomes a primitive class, the runtime warnings will be superseded by standard errors. At compile time, synchronization on certain types will also trigger a standard error.

Alternatives

We could abandon efforts to migrate these classes to be primitive classes. However, there are significant benefits that developers will enjoy when we complete the migration, and the relative impact on developers who depend on problematic behavior is quite small.

Dependencies

Migrating value-based classes to be primitive classes will require a reasonable amount of lead time with these warnings in place.

Most significantly, a JEP to make the wrapper classes primitive classes will depend on completion of this JEP.