JEP 401 (Value Objects) provides some general advice for migrating existing 
classes to be value classes. It’s a good starting point for anyone interested 
in the topic.

https://openjdk.org/jeps/401#Migrating-to-value-classes

As long as value classes are a preview feature, they can’t generally be adopted 
in the JDK libraries (which are not compiled with `--enable-preview`). However, 
we felt it was important for this JEP to provide users with a “starter kit” of 
some value classes out of the box, and to ease into any migration challenges 
for popular classes, so we identified a handful of JDK classes that the JEP can 
treat as value classes when preview features are enabled. (This works thanks to 
some heroics in the build system and deployment tooling.)

It’s worth asking which *other* JDK API classes might want to become value 
classes someday. I explored the question in conjunction with the CSR 
(JDK-8339199), and want to memorialize the experience here, for future 
reference.

Here's a definition of "value candidate" I've used for some analysis:
- Not an interface, enum, or anonymous class
- Not a "namespace class" (collection of static members with no other 
interesting code)
- No mutable fields
- No synchronized methods
- Has a value-candidate super (or Object)
- For our purposes here: public (up to the top level) and exported; final or 
abstract

A separate question that I didn’t explore: what about *implementation* classes? 
We can cast a wider net here, because these can be refactored—e.g., made final. 
Future work, perhaps something that will be more driven by targeted interest 
and high-priority performance opportunities.

Applying the above filter to java.base, I find:
- 1 record
- 89 concrete stateful classes
- 5 concrete stateless classes
- 94 abstract stateless classe
- 18 abstract stateful classes

Digging into these…


### 1 record

This is java.security.PEM. Most records can be made value classes, this is no 
exception. This is a new API (JEP 538), and has the distinction of being the 
only public record in java.base.

We might consider adding this to our “starter kit” list in a future preview 
release.


### 89 concrete stateful classes

28 of these are the classes we've already identified for migration.

8 of these are a bad fit for value classes (5 are an indirectly-mutable API; 2 
are pseudo-enums; 1 is an empty identity key; 1 is a singleton).

10 are javax classes I didn't look at.

4 of these are good value class candidates, but they implement Serializable so 
will need to wait for a migration plan.
- java.security.KeyPair
- java.time.temporal.ValueRange
- java.util.Currency
- java.util.UUID

That leaves:
- java.lang: StackWalker, Runtime.Version, ScopedValue.Carrier
- java.lang.constant: DynamicCallSiteDesc, EnumDesc, VarHandleDesc 
java.lang.invoke: SerializedLambda
- java.lang.module: ModuleDescriptor components (but not the class itself, 
because it has a cached hashcode); ResolvedModule
- java.math: MathContext
- java.net: PasswordAuthentication, UnixDomainSocketAddress, 
InetAddressResolver.LookupPolicy
- java.nio.charset: CoderResult
- java.security: 11 classes, including DomainLoadStoreParameter, some 
DrbgParameters and KeyStore nested classes; a few spec classes
- java.time: some internal classes: DateTimeFormatter, DecimalStyle, 
ZoneOffsetTransition, ZoneOffsetTransitionRule, ZoneRules
- java.util: HexFormat, RandomGeneratorFactory

From this list, RuntimeVersion and HexFormat are two that struck me as simple 
standalone APIs with private constructors that could readily be added to the 
migration list (although neither has much of a compelling performance use 
case). (I'd also add MathContext, except it has a public constructor, so is 
more of a compatibility risk.) Not coincidentally, these are also the few 
remaining identity classes that claim to be *value-based classes*. Like PEM, we 
could consider adding these to the “starter kit” in the future.

Most of the others are a small part of a bigger API. They may be migration 
candidates someday, but I think it would be best to tackle any value migration 
as a focused standalone effort for that API. That effort would also include 
thinking about near-miss candidates due to things like mutable fields that 
cache hashes.


### 5 concrete stateless classes

All singletons, should remain identity classes.

(A singleton doesn’t necessarily *need* identity, but… there’s only one of 
them, so it makes no difference either way.)


### 94 stateless abstract classes + 18 stateful abstract classes

2 of these are already identified for migration: Number and Record.

33 are part of a service provider API, which will generally involve singletons 
and is probably best to just exclude from consideration.

22 represent a mutable API, despite having no mutable fields.

7 have a private/sealed implementation, so we know would never have value 
subclasses (unless we decide to migrate those subclasses; usually it wouldn't 
make sense).

23 are in javax packages and I didn't dig into them.

That leaves 25 candidates:
- java.lang.classfile: CustomAttribute
- java.lang.constant: DynamicConstantDesc
- java.lang.module: ModuleReference
- java.net: 7 abstract classes that probably ought to be interfaces
- java.nio: Pipe, UserPrincipalLookupService, FileStore, Charset
- java.security: PKIXCertPathChecker, X509CRLEntry, CertPath, CRL, Permission
- java.text: Format, CollationKey
- java.util: AbstractCollection, AbstractSet

In the short term, for the “starter kit”, I think it may be worthwhile to 
eventually add AbstractCollection and AbstractSet. (But note that AbstractList 
has a protected mutable field; are we prepared to commit to not doing something 
like that in the future with these other collection classes?)

The others could be left to adopt the feature when it’s final, although none of 
them really jump out at me as obvious migration wins. (Not sure how many of 
them even care about public subclassing…)

Reply via email to