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…)
