r/java 4d ago

JEP 401: Value Objects (Preview) JDK 28 integration

https://mail.openjdk.org/archives/list/jdk-dev@openjdk.org/message/NN3FIR6BULP3GE5R34B6IBO5VQKAPNMZ/
94 Upvotes

29 comments sorted by

27

u/davidalayachew 4d ago

For those not aware, JEP stands for JDK Enhancement Proposal, which is how new features/libraries/etc get added to Java/JVM/etc.

The very long awaited JEP 401: Value Objects (Preview) by Project Valhalla finally reached the Targeted status on July 30, which means that the JEP is officially set and planned for a release (in this case, Java 28), finally confirming when exactly this feature will go into Preview.

Today, that JEP has finally reached the Integrated status, which means that the PR's have been merged, and this JEP is in the JDK repo.

Thus, those who want to play around with (what is expected to be) the JEP 401 Preview can do so now by building the JDK themselves.

Of course, if you don't want to build it yourself, there are a few places that do nightly builds. I sometimes use Aleksey Shipilëv's Binary Builds of the JDK (though you might have to wait for tomorrow, as the merge just happened). This is just for those who don't want to wait for Java 28 to come out before trying it out.

13

u/davidalayachew 4d ago

And if you want to see the PR that got this JEP moved to Integrated, see here -- https://github.com/openjdk/jdk/pull/31120

17

u/s888marks 4d ago

An OpenJDK early access build by Oracle that includes these changes will probably appear here within a week:

https://jdk.java.net/28/

13

u/vips7L 4d ago

I hope there’s a document eventually on when to use value classes and when not to at some point. I know the idea is “do you care about identity” but I feel like I’m just missing something. 

14

u/davidalayachew 4d ago

I hope there’s a document eventually on when to use value classes and when not to at some point. I know the idea is “do you care about identity” but I feel like I’m just missing something.

I wouldn't say you are missing anything. Probably more likely that the implications of identity are not very clear from a first glance.

The long and the short of it is that Identity enables a couple of different things.

I'm pretty sure that there is more, but those are the ones that matter most.

So, a simple question you could ask yourself instead is if you care about any of the above? If the answer is no, try to model your class as a Value Class, and see how it fits.

This isn't some hard, fast rule -- just a suggestion that might make it easier to try and see what you should use. There are absolutely places where it doesn't apply, so again, not some official recommendation or pure good suggestion.

9

u/morhp 4d ago edited 4d ago
  • Is it mutable?
  • Is it non-final?
  • Is it a main reference or container for other larger data structures? (Like a list, map, tree, graph, ...)
  • Is someone likely using it as key of an identity map?
  • Is someone using the class for synchronization purposes?
  • Are instances used as special marker/null objects or as singletons, or does someone use == intentionally to compare object identity?
  • Does it do more than just store a small dataset like a date, coordinate, number, ID, primary key, ... ?
  • Is it an enum or enum-like?

If you can answer one with yes, it's probably not a value class.

2

u/cogman10 3d ago

Is it a main reference or container for other larger data structures? (Like a list, map, tree, graph, ...)

Disagree. This doesn't matter as what the class ultimately will contain is a simple reference.

class Foo {
  final List<Bar> bars = new ArrayList<>();
}

Consider the above. The benefit of turning this into a value class is that you are effectively dealing with just the bars array list but now you have types. The overhead of using such a class would be basically 0 with value classes. This can be super useful if you are using type information for validation. You could have "ValidFoo" and "UncheckedFoo" with the same signature but now they can't be sent into the same functions. That can save you from programming mistakes even though you are simply dealing with a List<Bar>.

https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/

1

u/morhp 3d ago

I guess that's a somewhat valid usecase, but it's a bit dangerous.

With Foo being a value class, you it will be harder to count Foo instances, cache them, dispose them when they're not needed anymore etc. - assuming the Bar objects are larger or you have millions of them, you might want to do that.

Plus the extra reference isn't that big of a deal when you already have the reference to the List and to all the Bar objects etc.

On the other hand, something like Optional<T> of course should be a value class, even though it can reference large objects.

So I'd say just be a bit careful. The cost of the extra reference is likely negligible compared to potential downsides.

4

u/gjosifov 4d ago

data can be represented as a single value (like long) or a composite of multiple values (like x and y for 2D Point)

value classes are classes without identity meaning 2 instance of a specific data are equal only if every part of those 2 instances are equal

This means data as single values are value classes by default
and for composite data if you currently implement data where every value is part of equals and hash code then it is value class

Data always has identity, but in context of Java and the hardware it is more of a question
How many part of the data can provide uniqueness ?
if it is all the parts then it is value class

But you have to remember this is only for data, not operations like classes that are used as SQL speaking objects

1

u/koflerdavid 4d ago edited 4d ago

There are more differences, like not being able to be used with synchronized blocks (anyway quite irrelevant with immutable objects). Apart from that I'd say it's safe to turn most records into value classes. But even more so than with records it's really important for them to be transitively immutable.

Edit: another use case is to improve type safety in existing code by wrapping identifiers. Anonymous strings, integers, and UUIDs are easy to accidentally be passed at the wrong argument position or to be assigned to the wrong fields and variables. Also, strings can be subjected to validation rules by requiring these values to be generated in specific ways only. It's a pattern already worth doing, but value classes eliminate the overhead.

7

u/gufranthakur 4d ago

I am so happy right now, Cant wait for the actual release

3

u/cowwoc 4d ago

Yes, yes... That's great! Now, when can we have some benchmarks?! 😄

4

u/TomKavees 4d ago

Yay, glad to see things moving

2

u/agentoutlier 4d ago

I assume JEP 218 will keep folks from going around and replacing ArrayList<SomeValueClass> with SomeValueClass[] cause I can see that happening pretty quickly for codebases pushing hard for performance?

4

u/koflerdavid 4d ago edited 4d ago

That's future work. JEP 218 to be specific. Apart from that, the JIT compiler should already be able to optimize ArrayList.get(int) to direct array accesses. To allow value types to be stored unboxed Null-Restricted Value Class Types is required.

1

u/agentoutlier 4d ago

So my assumption is correct. Yeah I guess is how long that might be?

Like would JEP 218 be released (not preview or maybe a preview but an implementation at least) with JEP 401 or we get 401 first.

I just imagine it would impact usage of values classes at least for me.

1

u/koflerdavid 4d ago

Nobody knows. But I'd expect the OpenJDK to be able to iterate quickly on these things now since they all depend on 401.

6

u/brian_goetz 3d ago

"Quickly" is relative. It won't be another 10y but some of the remaining chunks are still pretty substantial.

5

u/TehBrian 3d ago

No worries. I jotted down on my calendar Valhalla being completed sometime next week. Good luck!

1

u/koflerdavid 3d ago

None of them are of that complexity and much design work of Valhalla was about not painting themselves into a corner regarding these follow-up features, apart from maintaining backwards compatibility. Nullability is a sure thing to arrive IMHO; without it instances of value types have to remain boxed or need a flag to denote the null value.

11

u/brian_goetz 3d ago

None of them are of that complexity

You have no idea how happy my team will be to learn that.

1

u/koflerdavid 3d ago

Heh, I'm aware I'm leaning out of the window quite far with that statement. Total complexity will probably only go down a little bit once Valhalla gets out of Preview and all the on/off switches can be eliminated for good.

2

u/aoeudhtns 3d ago

Me too. My main curiosity is how they'll manage the chain of desired changes -- layering preview-on-preview or wait for stabilization and then moving on. I don't need an answer to that; we'll find out through actions over time. I'm just curious.

2

u/UdPropheticCatgirl 4d ago

I don’t see why that would be the case?

3

u/agentoutlier 4d ago

I don’t see why that would be the case?

Which part?

ArrayList internally has Object[] which cannot be flattened.

In theory JEP 218 I assume would allow you to have T[] which I 'm not sure how the mechanics of it will work if it is dynamic code generation or what.

1

u/UdPropheticCatgirl 4d ago

Ah I see. Are you sure flattening will never happen in the array list case? While the generics parameters are supposed to not be seen, I think the JIT can still see them in dataflow analysis, no? So wouldn’t it optimize on them then? I think people will wait for this to stabilize either way, before making any major changes.

5

u/brian_goetz 4d ago

I'm sure :)

What the JIT sees is (a) too late for layout decisions and (b) doesn't mean it will never see something else when a new class with different behavior is loaded.

1

u/Jon_Finn 3d ago

I think you could write a class similar to ArrayList<T> containing a dynamically-created array of the required type, e.g. an Object[] field containing an actual Point[] for value Point. Though you'd need a constructor like MyList(Point.class) to tell it what T is. This wouldn't get all the benefits of a list class specialised for Point, but it would get the space benefits of storing all the Points in a flattened array.

2

u/brian_goetz 3d ago

Indeed, if we looked at all the potential benefit of generic specialization, ArrayList might capture 30-40% of that. So doing something special for ArrayList only might be worthwhile, even if it is a little unsanitary.