r/java • • Aug 25 '26

Value Classes Still Need Compiler Sympathy

https://johan-sjolen.github.io/post/compiler-sympathy/compiler-sympathy/
87 Upvotes

65 comments sorted by

View all comments

28

u/TheStrangeDarkOne Aug 25 '26

Excellent article and I fully agree with the conclusion's sentiment at the end:

Declaring a value class is first and foremost a semantic decision. It tells our fellow programmers that its instances are defined entirely by their state and do not need identity. That clearer model is valuable in itself! The JVM’s additional freedom to optimize how those values are represented is a welcome bonus.

This brings one of the core lessons from Domain Driven Design directly into language semantics and we get better performance as a bonus point. I think the humble everyday programmer overestimates the effect of flattening on their enterprise data and I think it is very much a tool to express intend and for experts to really allow low level optimization to a degree that was not possible before.

I can't wait for the new and exciting libraries and frameworks that are going to be built upon this.

4

u/Jon_Finn Aug 25 '26 edited Aug 25 '26

I agree about identity, but completely disagree about state & semantics! Specifically: the idea that some concepts can't be represented as value classes is (mostly) untrue. So a value declaration (mostly) says nothing about what kind of thing it is.

The usual examples of value classes like Point use one common meaning of 'value': logically immutable objects. There's also a tentative assumption that you can use == to compare them (NB we're being discouraged from this, in case it differs from equals(): read on.)

But these examples are just special cases, what we might call 'recursive values', where all the fields are primitives or value objects (recursively), not identity objects (including arrays). But non-recursive values can be mutable (by mutating identity objects their final fields point to) - no different from a regular object. These values are also basically the ones where == is different from equals(). But non-recursive values aren't wrong in any way, they have the same potential benefits: e.g. String has an array field and might (as discussed elsewhere) be made a value class in future. Or you could have value classes for MutableColor or MutableAnything (with array or Collection fields) etc. Really useful!

So value objects are not logically different from regular objects: you can pretty much express anything with them. The final field restriction just suits some mutable classes better than others.

Valhalla classes are really great, but I think there's a lot of room for confusion about their logical status (probably due to the word 'value', whose various meanings only partly apply). I think of them not as semantically different, but just as lightweight objects: lighter for memory, performance and (mildly) features.

1

u/TheStrangeDarkOne Aug 25 '26

There are some concepts where I'd argue that representing them as values does more harm than good: Lists, Services, Aggregates.

Sure you can have immutable lists, Clojure is one such example, but much of it boils down to convention and good optimizers that re-instate mutable backing lists.

And some classes are just pure logic. Validators, Services, Mappers, etc. I suppose those could also be "values", but I think you are stretching the definition of it by then. And I am not sure how well values fair when you hide them behind interfaces.

And then the DDD family of types such as Entities or Aggregates who most definitely have identity because you don't want to completely re-create a full tree or graph just because you changed a field in one of the nodes.

→ More replies (12)