-
Sub-task
-
Resolution: Unresolved
-
P4
-
None
-
None
-
None
This task summarizes the standard library changes introduced by JEP 401.
### Object methods
Most of the methods of java.lang.Object are fine as is, but their documentation may need to be updated.
'equals', 'hashCode', and 'System.identityHashCode' rely on a revised definition of "the same object" and "distinct objects", where two value objects are "the same" if they belong to the same class and have the same field values. (The implementation is supported by java.lang.runtime.ValueObjectMethods.)
'clone' is useful for value objects if they store an identity object in a field. If not, the result should generally be == to the original. In either case, the implementation of 'Object.clone' will either throw CNSE or simply return 'this'.
'toString' works out of the box, but the printed hash code reflects the lack of identity (as noted above).
'wait'/'notify'/etc. work out of the box; they always throw IMSE, because it's impossible (per language/VM rules) to 'synchronize' on a value object.
'finalize': will never be called on value objects, but nothing prevents overriding it
### Core reflection
New preview API methods in `java.util.Objects`: `hasIdentity` and `requireIdentity`. These test/assert whether an object is an identity object. Arrays and direct instances of the class `Object` are always considered identity objects. (We may consider a similar method as a member of class `Object`.)
A new `IdentityException` class is declared to indicate that an operation fails because an object does not have identity.
`java.lang.reflect.Modifier` and `java.lang.reflect.AccessFlag` add support for the `value`/`ACC_IDENTITY` and `ACC_STRICT` flags. This is also exposed via new `isValue` and `isIdentity` methods in `java.lang.Class`.
(Arguably, `obj.getClass().isIdentity()` is a good enough way to express `hasIdentity(obj)`, and the latter is redundant. But identity is so meaningful for the object itself, nevermind its class, that a more direct query seems justified.)
### java.lang.invoke
VarHandles and MethodHandles that access flattened storage must interpret it properly. This is supported through new Unsafe methods that interact with flattened storage.
(There is no expectation that these APIs will support scalarized or null-restricted entry points. We leave those sorts of optimizations to inlining.)
### Serialization
ObjectInputStream.readObject generally throws an InvalidClassException if the object being deserialized is an instance of a value class, whether concrete or abstract.
There are four exceptions:
- Value records are deserialized following the normal record deserialization process.
- A non-Serializable abstract value class can act as a superclass if, as usual, it can be instantiated via a no-arg constructor call.
- The primitive wrapper classes get special treatment and are deserialized via a constructor or 'valueOf' call.
- The class Number gets special treatment—it has no fields and no constructor logic, so deserialization can simply skip it.
ObjectOutputStream.writeObject should detect this problem and throw an InvalidClassException when writing an object that implements a value class and isn't one of the allowed special cases.
(Users declaring non-record, Serializable value classes can work around this restriction by using the 'writeReplace' mechanism. In this release, there is no other support for migrating a Serializable identity class to be a value class.)
### java.lang.ref and WeakHashMap
Instances of java.lang.ref.Reference throw an exception if created for a value object. Similarly, WeakHashMap rejects value objects as keys. (In a future release we may provide some mechanism for GC management of the identity objects referenced by a value object.)
### java.lang.classfile
The class file changes, as described in JDK-8317278, should be supported by the java.lang.classfile API. Specifically:
- Support modifiers `ACC_IDENTITY` and `ACC_STRICT` in the appropriate contexts
- Recognize the `LoadableDescriptors` attribute
### Migrated value-based classes
The following classes should be capable of being treated as value classes by adding a 'value' keyword:
- java.lang.Number
- java.lang.Record
- All 8 primitive wrapper classes
- All 4 java.util.Optional* classes
- 12 java.time classes: Duration, Instant, LocalTime, Year, YearMonth, MonthDay, Period, LocalDate, LocalDateTime, OffsetTime, OffsetDateTime, ZonedDateTime
- 5 java.time.chrono classes: ChronoLocalDateImpl, MinguoDate, HijrahDate, JapaneseDate, and ThaiBuddhistDate
Specifically, their constructors should be compatible with strict fields, with an implicit 'super()' call being placed at the end of a constructor body.
The 'month' and 'day' fields of YearMonth, MonthDay, LocalDate, HijrahDate, and JapaneseDate should be refactored to have type 'byte' instead of 'int'.
In JDK builds, alternative sources of these classes are generated, inserting the 'value' modifier, and then recompiled and made available at run time whenever '--enable-preview' is set.
### Object methods
Most of the methods of java.lang.Object are fine as is, but their documentation may need to be updated.
'equals', 'hashCode', and 'System.identityHashCode' rely on a revised definition of "the same object" and "distinct objects", where two value objects are "the same" if they belong to the same class and have the same field values. (The implementation is supported by java.lang.runtime.ValueObjectMethods.)
'clone' is useful for value objects if they store an identity object in a field. If not, the result should generally be == to the original. In either case, the implementation of 'Object.clone' will either throw CNSE or simply return 'this'.
'toString' works out of the box, but the printed hash code reflects the lack of identity (as noted above).
'wait'/'notify'/etc. work out of the box; they always throw IMSE, because it's impossible (per language/VM rules) to 'synchronize' on a value object.
'finalize': will never be called on value objects, but nothing prevents overriding it
### Core reflection
New preview API methods in `java.util.Objects`: `hasIdentity` and `requireIdentity`. These test/assert whether an object is an identity object. Arrays and direct instances of the class `Object` are always considered identity objects. (We may consider a similar method as a member of class `Object`.)
A new `IdentityException` class is declared to indicate that an operation fails because an object does not have identity.
`java.lang.reflect.Modifier` and `java.lang.reflect.AccessFlag` add support for the `value`/`ACC_IDENTITY` and `ACC_STRICT` flags. This is also exposed via new `isValue` and `isIdentity` methods in `java.lang.Class`.
(Arguably, `obj.getClass().isIdentity()` is a good enough way to express `hasIdentity(obj)`, and the latter is redundant. But identity is so meaningful for the object itself, nevermind its class, that a more direct query seems justified.)
### java.lang.invoke
VarHandles and MethodHandles that access flattened storage must interpret it properly. This is supported through new Unsafe methods that interact with flattened storage.
(There is no expectation that these APIs will support scalarized or null-restricted entry points. We leave those sorts of optimizations to inlining.)
### Serialization
ObjectInputStream.readObject generally throws an InvalidClassException if the object being deserialized is an instance of a value class, whether concrete or abstract.
There are four exceptions:
- Value records are deserialized following the normal record deserialization process.
- A non-Serializable abstract value class can act as a superclass if, as usual, it can be instantiated via a no-arg constructor call.
- The primitive wrapper classes get special treatment and are deserialized via a constructor or 'valueOf' call.
- The class Number gets special treatment—it has no fields and no constructor logic, so deserialization can simply skip it.
ObjectOutputStream.writeObject should detect this problem and throw an InvalidClassException when writing an object that implements a value class and isn't one of the allowed special cases.
(Users declaring non-record, Serializable value classes can work around this restriction by using the 'writeReplace' mechanism. In this release, there is no other support for migrating a Serializable identity class to be a value class.)
### java.lang.ref and WeakHashMap
Instances of java.lang.ref.Reference throw an exception if created for a value object. Similarly, WeakHashMap rejects value objects as keys. (In a future release we may provide some mechanism for GC management of the identity objects referenced by a value object.)
### java.lang.classfile
The class file changes, as described in JDK-8317278, should be supported by the java.lang.classfile API. Specifically:
- Support modifiers `ACC_IDENTITY` and `ACC_STRICT` in the appropriate contexts
- Recognize the `LoadableDescriptors` attribute
### Migrated value-based classes
The following classes should be capable of being treated as value classes by adding a 'value' keyword:
- java.lang.Number
- java.lang.Record
- All 8 primitive wrapper classes
- All 4 java.util.Optional* classes
- 12 java.time classes: Duration, Instant, LocalTime, Year, YearMonth, MonthDay, Period, LocalDate, LocalDateTime, OffsetTime, OffsetDateTime, ZonedDateTime
- 5 java.time.chrono classes: ChronoLocalDateImpl, MinguoDate, HijrahDate, JapaneseDate, and ThaiBuddhistDate
Specifically, their constructors should be compatible with strict fields, with an implicit 'super()' call being placed at the end of a constructor body.
The 'month' and 'day' fields of YearMonth, MonthDay, LocalDate, HijrahDate, and JapaneseDate should be refactored to have type 'byte' instead of 'int'.
In JDK builds, alternative sources of these classes are generated, inserting the 'value' modifier, and then recompiled and made available at run time whenever '--enable-preview' is set.
- csr for
-
JDK-8339199 Standard library implementation of value classes and objects
- Draft
- relates to
-
JDK-8334742 Change java.time month/day field types to 'byte'
- Open
-
JDK-8326719 [lworld] Migrate core-libs support to JEP 401 (part 1)
- Resolved
-
JDK-8326867 [lworld] remove Q-descriptor support from jdk.experimental.bytecode test library
- Resolved
-
JDK-8260606 Update Valhalla core-libs naming for methods related to primitive classes
- Resolved
-
JDK-8269096 Add java.util.Objects.newIdentity method
- Closed
(1 relates to)