JEP 401: Value Objects (Preview) merged to OpenJDK master
Posted by mfiguiere 3 days ago
Comments
Comment by timmg 3 days ago
The lack of value types is the biggest impediment to certain types of performance. I am really looking forward to this evolution of the language.
Comment by DarkNova6 3 days ago
1: Classical Object
2: Object without identity
3: Atomic Value
4: Classical Value (Tearable)
Each bucket has clearly defined performance characteristics and semantic constraints. So depending on the nature of your data, you can always get some benefit from values even if you couldn't go all the way. This is a work of art.
Comment by chambers 3 days ago
On the note of science, I'll credit Java's maintainers for treating their language as a technology and not merely as a product. Many languages respect the real-world physics of computers to stay relevant. Few respect the "metaphysics" until weird bugs keep emerging in the compiler and ecosystem. Bugs that can't be solved with more building, engineering, or even design. Because those bugs are not defects; they come from the concepts & axioms of the language itself, not its implementation.
Comment by vlovich123 3 days ago
That’s a lot of complexity a lot of other languages don’t even need. It’s great Java designers have managed to shoe horn something in, but it’s all because of misdesigns 30ish years ago mispredicting of computer architecture.
Comment by pron 3 days ago
The idea is that instead of controlling memory layout and referencing directly, you communicate your intent: do you care about this value's identity or not? Do you need atomicity or not? Do you need nullability or not? Once the intent is clear, the compiler is free to choose the most appropriate and efficient layout for the particular value at the particular use site. In other words, you say what semantics you're interested in rather than how to implement them at the lowest level.
This opens up optimisation opportunities that are lost when the programmer directly controls the representation rather than the intent. E.g. in other languages you may say whether you want to pass some value by reference or by value. Here the compiler is free to say, well, if identity and nullability are not needed here, I can either pass by value or by reference, and I'll do whichever is more efficient.
And by the way, your point about early misdesign (whether it applies here or not) also inverts the desired state. Every language makes decisions that will be suboptimal in the environment some time later, and Java certainly has its share (its mutability and nullability default; how it treats serialisation). Rust, for example, was first designed twenty years ago, and some of its fundamental decisions reflect the state of the world at that time (it went all-in on some C++ premises that seemed fine 20 years ago). But since important codebases often outlast the outdatendness of early language decisions (your OS and your browser are running some >30yo code and/or affected by >30yo design decisions), one of the things most important in a language isn't the decisions it makes early on - some will prove "wrong" while your codebase is still alive and kicking - but how well it adapts and evolves. So when you pick a language for an important project today, the language's current state will end up mattering less than how the language evolves in the future. The question asked isn't "will I like this decision today?" but "will I regret this decision ten years from now?" Java is one of the languages with the lowest "regret factor", possibly lowest of them all.
Comment by dzaima 3 days ago
Comment by DarkNova6 3 days ago
Comment by DarkNova6 3 days ago
Nothing, absolutely nothing about the design is a compromise that would detract from the project if it was for any other language.
You could judge them for bringing “complexity to an easy topic” but it really is far from that and the idea of a value type being a distinct dichotomy from an object is just a convenient tradition.
The push for immutability and value-ness has been going on for a long time and the motivation of the project was to bring performance benefits that align with semantic statements about the underlying data type. Which is of course a more nuanced take than “go value, go faster”.
Comment by vlovich123 2 days ago
Comment by DarkNova6 2 days ago
Comment by daviddoran 3 days ago
Comment by germandiago 3 days ago
But nowadays I see the language improved a lot, avoided async/await and added virtual threads, added records, now Valhalla, decent lambdas and tons of quality of life improvements.
In the meantime C# became a monster of 2000 ways to declare properties init get, set, backed, not backed, stackalloc, structs, etc.
And I know it is done for performance: but Java is still you have a value type or a reference type. That simple. And now, on top of it Valhalla.
I think Java stayed overall more stable and simple.
C# is still pretty good, but it is just more complicated.
Comment by MrBuddyCasino 3 days ago
Do I love Spring? No. Do I prefer Typescript for my little side projects? Yes.
Comment by DarkNova6 3 days ago
Comment by ksec 2 days ago
Comment by vips7L 1 day ago
Comment by MrBuddyCasino 3 days ago
Comment by merlindru 3 days ago
the problem with it is entirely cultural. why is `IStatusChangeEntityCreationManagerFactory` everywhere?
and why are the frameworks so huge and all encompassing? i think batteries included rails-style frameworks are great, but when i wrote Java, it didn't feel like rails at all
none of these are language problems
Comment by cryptos 3 days ago
Comment by spockz 3 days ago
On the other hand I have used Quarkus which was great until it wasn’t because a certain http feature wasn’t supported or we needed integration with an unsupported database. In enterprise that is enough reason to just choose for the batteries included option and lift along on improvements of the main framework.
Comment by za3faran 3 days ago
How difficult is/was it to add a custom integration?
Comment by spockz 2 days ago
At this point the work is typically more than you want a single team to maintain. Unfortunately, in many large enterprises it is just easier and more efficient to grab a bigger framework than trying to organise sharing the custom integration in the organisation.
Comment by mands 3 days ago
I'm back on Spring for work reasons but use it for all my side-projects.
Comment by duckydude20 3 days ago
Comment by germandiago 3 days ago
Comment by stickfigure 3 days ago
Java is a big tent. Yeah there are some parts that deserve this kind of mockery.
The Rails equivalent is over-metaprogramming. "I solved it with method_missing." I've worked on code like this in real life. TBH I'd rather have the inane verbose class names.
Comment by dionian 3 days ago
Comment by stickfigure 2 days ago
Comment by paulddraper 2 days ago
Manager // interface
ManagerImpl // classComment by paulddraper 2 days ago
Comment by ibejoeb 3 days ago
I had to support systems from IBM, BEA, Apache, Red Hat, SAP, and some I'm certainly forgotten. Then there were the piecemeal systems. OSGi made an appearance. Then you have databases from Oracle, Microsoft, and IBM. Identity and auth was the wild west, with countless SSO vendors, wacky LDAP implementations, wackier SAML implementations, Kerberos, Ping... Despite that, it was pretty feasible to build and ship the same artifacts and configure things at the site.
Comment by jillesvangurp 3 days ago
A lot of the Java stuff actually came out of the Smalltalk community. IBM used to employ a lot of these people and they brought their enterprise stuff with them when things like Enterprise Java Beans became a thing. And because IBM was there, you got everybody else showing up as well. That's why Oracle bought Sun as well to get access to that juicy market.
IBM could have picked any language. But Java was kind of there and new at the time and it kind of did the job. So that's what they jumped on. MS stole a bit of the thunder with dot Net a few years later which is why C# is also very common in that space. But besides that there isn't a whole lot that has similarly wide adoption. Client side is a whole different story of course.
And the reason stuff gets complicated is that there is a lot of design by committee going on in this world by parties that are mutually adversarial and have big budgets.
So, this particular feature comes out of at least 15 (I think) years or so being debated in that sort of committee. It kind of predates the whole inception creation of things like Kotlin. I was at a 15 year birthday party for that at the Jetbrains office in Berlin a few weeks ago. Debates about value classes in the Java community probably go back to last century.
Comment by vlovich123 3 days ago
And yet a lot of enterprises have done without in other languages.
Comment by nogridbag 3 days ago
I have seen a surge of overly complicated PRs now that we're in the AI era, but it's our job to be the gatekeeper and push back on this crud from entering the codebase. And that's likely true of any language.
Comment by MBCook 3 days ago
Comment by peterashford 3 days ago
Comment by dminik 3 days ago
Comment by vips7L 3 days ago
Comment by dminik 2 days ago
Comment by vips7L 2 days ago
Comment by dminik 1 day ago
I read that comment as "Show me where [long classnames] are in the language, standard library or <relevant ecosystem project>."
Spring is one of the biggest java libraries and quite widely used. I think it fits rather nicely, even if the example itself is fairly old.
Comment by vips7L 1 day ago
Comment by dminik 1 day ago
https://survey.stackoverflow.co/2025/technology#1-web-framew...
Comment by peterashford 1 day ago
Comment by dminik 2 hours ago
https://docs.oracle.com/en/java/javase/23/docs/api/java.base...
Or this 6 word beast: https://docs.oracle.com/en/java/javase/21/docs/api/jdk.dynal...
This is probably the most enterprisey sounding, but not the longest class I could find: https://docs.oracle.com/javase/8/docs/api/javax/naming/spi/I...
Comment by ModernMech 3 days ago
Comment by peterashford 1 day ago
Comment by merlindru 2 days ago
Comment by shepherdjerred 3 days ago
I don’t reach for it much anyone since I prefer TypeScript’s type system and the ability to use the same language for both client/server, but I’m never upset to write some Java :)
Comment by ktosobcy 3 days ago
And I appreciate and I'm under huge awe to the amount of though put into the evolution -- instead of just cramming every new shiny thing they ponder it a lot to arrive at the most sensible option while trying their best to maintain backward compatibility -- kudos!
Comment by jameslars 2 days ago
Comment by MBCook 3 days ago
I mostly ignore online discussions about Java. So much of it just feels like memes. Complaining about things that haven’t been valid in a very long time. Sometimes almost 2 decades.
It feels a bit like people bashing Nickelback. It was the “cool” thing to do. Maybe they’ve never used Java. Or only for one college course.
People can dislike Java. I’m sure some do. It’s fine. But it seems like the anti opinion is really over represented/amplified.
Comment by znpy 3 days ago
I really really think that java is weighted down by all the ceremonial enterprise stuff that has accumulated over the years.
Lightweight frameworks are somewhat recent, otherwise you really couldn’t print a text/plain http response without bringing up a whole application server and a whole ecosystem of libraries (j2ee)
Also… the build system situation is still shit
Comment by jiehong 3 days ago
I wish it came with more batteries included, and it renovated that old Java cli split per commands, following a bit what golang did:
- java run
- java build
- java test
- java fuzz-test
- java format
- java lock
- java lsp
- etc.
Along with a better support of posix long form flags like ‘--version’.
Comment by speed_spread 3 days ago
Comment by vips7L 3 days ago
Comment by speed_spread 2 days ago
Comment by tombert 3 days ago
For most of my career, I thought I hated Java, primarily because every piece of Java code I had ever worked with was overly verbose across a million different files to do things that would take like ten lines of code in any other language.
Then I actually started looking at all the features for Java 8, 11, 17, and 21, and realized: NO! Java actually gives lots of really great tools and language features that allow you to write relatively pretty, terse, performant code. Record types help cut down on a lot of boilerplate stuff, the streams API allows for pretty work over lists, sealed interfaces mostly solve my itch for ADTs, virtual threads are genuinely pretty impressive bits of engineering, and for the stuff that isn't built in, Vert.x and Disruptor do a pretty good job filling in the gaps.
Once I learned all that, I was kind of mad at people still writing code like it was 1999, but also started to enjoy writing Java. It helps that by this time I was already senior and staff level, meaning I was given much more leeway in how code was written (so the poor junior engineers are stuck/blessed dealing with my code using all the shiny new features).
Comment by geodel 3 days ago
For companies that deployed Java this is the best part. Growth of Java from 1995-2015 and enterprise IT outsourcing was almost in lockstep. It was not for programmers with taste or interest in coding. It was J2EE 1000 page manual printed and distributed in bulk at IT vendors describing things in hugely verbose details with dummy pet-store project to use all facets of Java/J2EE that ingrained into overall Java culture.
It could be business or politeness reasons that well meaning Java language authors rarely spoke a word against this wider Java culture. So it was just assumed that overtly verbose code and organization of Java projects have official stamp to it.
Comment by tombert 3 days ago
When I worked at a BigCo [1], our project was a big monorepo in Java which had 60,000 folders, and I don't remember how many files. It was decidedly no fun to work on, because everything was spread across a million files and so doing anything required sifting through dozens or even hundreds of files. I hated it.
I've worked on giant projects since then, but they've been written in more modern Java and they are less painful.
[1] It's not hard to find the specific one through my history but I politely ask you do not post it in reply to this post.
Comment by deepsun 3 days ago
Whether that generalization is premature -- I don't know, just my observation on why.
Comment by tombert 3 days ago
I've written plugin systems for stuff I've worked on, and it didn't require nearly the same level of verbosity. Granted, I usually have done this with sockets instead of directly using JVM and reflection. To each their own, but I like using sockets (usually ZeroMQ) for this stuff because then it's completely language independent.
Even if I granted that you needed to use all this factory and builder glue stuff, I don't agree with the typical Java developer's strategy of breaking things up across a million files, which Java at least greatly encourages.
Comment by Rohansi 3 days ago
It's more that it's easy to write code that links directly into the game. Easy to do because you can decompile them and make changes easily without having source code. Sure, Minecraft was obfuscated for a long time, but people put a lot of effort into deobfuscation and obfuscation removes a lot less than native compilation.
(You also don't need an official modding API this way)
Comment by Flowdalic 3 days ago
Java remains unfairly tethered to a reputation forged over 20 years ago. Human psychology reveals a reluctance to update our worldview: once a collective consensus solidifies, it becomes very difficult to dismantle. Yet, to dismiss Java today is to overlook a remarkable evolution. Modern Java is an elegant, highly capable language, resting upon the Java Virtual Machine, which is a remarkable piece of technology.
Comment by mormegil 3 days ago
Comment by cogman10 3 days ago
With this finally integrated, we are also likely to start seeing pretty regular performance bumps for all things value related.
Comment by ludovicianul 3 days ago
Comment by germandiago 3 days ago
C++ gets a lotof rage for doing the same thing.
The last thing you want in an enterprise environment is that your working code breaks and you have to commit more time to things that were working before.
Comment by cogman10 3 days ago
For example, this is technically legal in Java and will probably work today. Both foo and bar will synchronize on the same referenced object due to the integer cache.
This is something that will break when value types land for real.
void foo() {
Integer i = 1;
synchronized(i) {
doEvil();
}
}
void bar() {
Integer i = 1;
synchronized(i) {
doEvil();
}
}Comment by germandiago 3 days ago
I think that, with all things on the table, backwards compatibility, a spec and, where things are broken things are signaled should be the standard for enterprise grade non-toy apps.
Of course, breaking something should be kept to the minimum.
That is why I keep using C++ and, if I have to do backend in the future for webs, I will probably choose Java over C#: stable, multi-vendor, will work in 10 years, stable.
Comment by cogman10 3 days ago
You can be pretty sure that a jar compiled for Java 1.0 will work on a Java 25 VM, and that's pretty great.
Comment by groundzeros2015 3 days ago
Comment by germandiago 3 days ago
C++ is very different since it is native.
Java is a VM.
This affects lots of decisions in weird ways.
But in essence both commit to backwards stability at the end.
C++ goes for performance, JVM for a more full-featured bytecode vm, etc. but still.
Comment by pron 3 days ago
Comment by germandiago 2 days ago
I meant the kind of "perf-to-the-instruction" thst native can generate vs other considerations.
Comment by pron 2 minutes ago
Comment by groundzeros2015 3 days ago
Comment by cogman10 3 days ago
They let the likes of Kotlin, C#, C++, Scala, Haskell, etc all explore their own little features then they figure out if it could be integrated with Java and what it'd look like.
I really like that approach because it's slow, methodical, and the features typically are very well aligned with Java. None of them feel particularly out of place.
Java's preview system also works pretty well to get these features refined over time.
Comment by exabrial 3 days ago
Comment by dionian 3 days ago
Comment by spopejoy 1 day ago
Comment by vips7L 3 days ago
Comment by swiftcoder 3 days ago
Comment by sheept 3 days ago
- Java has long had a modern replacement for Date, while JavaScript's recently standardized Temporal API still isn't supported in Safari.
- Java has switch expressions, while JavaScript, despite its Scheme influence, does not.
- And now Java is getting value objects, while JavaScript's equivalent tuples & records proposal has been withdrawn.[0]
JavaScript was not developed as a fork of Java, but much of its basic syntax still resembles Java, so I think it's fair to compare them.
Comment by ivolimmen 3 days ago
Comment by germandiago 3 days ago
"std::allocator is to allocation what std::vector is to vexation".
There was another transparency inside:
"std::allocator is to allocation what alligator is to allegation".
Comment by kleiba2 3 days ago
You mean the sibling who commented five minutes before you?
Comment by Yokolos 3 days ago
https://www.keycdn.com/support/difference-between-java-and-j...
Comment by hawk_ 3 days ago
Comment by nalekberov 3 days ago
It's absolutely irrelevant to compare these two languages. Calling the former "Javascript" was just a marketing trick.
Comment by cesarb 3 days ago
Playing devil's advocate: back when you still could use Java in the browser, you could use Javascript to script your Java applets.
Comment by inigyou 3 days ago
Comment by ErenayDev 3 days ago
i think Temporal API added in latest preview version of safari.
https://webkit.org/blog/18182/release-notes-for-safari-techn...
Comment by grishka 3 days ago
Comment by Altern4tiveAcc 3 days ago
Comment by LoganDark 3 days ago
Comment by dwaite 3 days ago
Note Temporal was in the Ecmascript 2026 release back in March.
Comment by weinzierl 3 days ago
I do not have a lot of knowledge about the details but from the outset it seems like a rather bold move to me.
Comment by agilob 3 days ago
Comment by swiftcoder 3 days ago
I learned this the hard way, when a C++ JNI extension I was working on accidentally overwrote the pooled value for zero, and all hell broke loose...
Comment by agilob 3 days ago
The cache can be extended on startup with env var `java.lang.Integer.IntegerCache.high `
https://github.com/openjdk/jdk/blob/cc278dbb8a1ca0754d584270...
Comment by pwagland 3 days ago
Comment by tpm 3 days ago
https://thedailywtf.com/articles/Disgruntled-Bomb-Java-Editi...
Comment by J-Kuhn 3 days ago
In this example, it would throw an IllegalAccessException, because java.base doesn't open java.lang.
Comment by vips7L 1 day ago
Comment by inigyou 3 days ago
True = False
and of course in JavaScript undefined = "a string"Comment by swiftcoder 2 days ago
Comment by josefx 3 days ago
Comment by ivolimmen 3 days ago
Comment by titzer 3 days ago
Comment by ivanjermakov 3 days ago
> JEP 218, Generics over Primitive Types (with revisions), will allow generic classes and methods to specialize field, array, and local variable layouts when parameterized by value class types.
Ok now this is cool! JVM needs more ways to use compile time known value sizes for better memory allocation and optimizations.
Comment by germandiago 3 days ago
Comment by groundzeros2015 3 days ago
Comment by brunoborges 3 days ago
Comment by groundzeros2015 2 days ago
Comment by brunoborges 4 hours ago
I strongly suggest watching some of their talks from JVMLS (JVM Language Summit), especially the ones from Brian Goetz and Mark Reinhold.
https://www.youtube.com/playlist?list=PLX8CzqL3ArzUOgZpIX6Gs...
Comment by andrybak 3 days ago
The final singular commit is cc278db (8389219: Implement JEP 401: Value Objects (Preview), 2026-07-31) https://github.com/openjdk/jdk/commit/cc278dbb8a1ca0754d5842...
Comment by fulafel 3 days ago
Comment by jerven 3 days ago
Comment by fulafel 3 days ago
edit: rereading, I guess the implication is that there's tooling built on top of Github outside of Git, so Git history is indeed not meant to be contain some things.
Comment by J-Kuhn 3 days ago
* Contributors without github accounts * Moving away from github (when needed) * No vendor lock-in for github stuff.
I personally think, this is a good idea - a lot of work, but may be worth it in the long run.
Comment by thibaut_barrere 3 days ago
Comment by germandiago 3 days ago
Also, now on the Java side: I think they made a great job avoiding async/await and using virtual threads. Amazing results.
I also like all the work done around structured concurrency.
Comment by estebank 3 days ago
Comment by jurgenburgen 3 days ago
Comment by DarkNova6 3 days ago
It started out as a highly invasive, highly disruptive and highly experimental branch that eventually turned into a beautiful specification that put semantics and performance payoff first and syntax seconds.
Comment by krzyk 3 days ago
Comment by swaranga 3 days ago
Why does the class author have to declare it as a value type? I know this has been rehashed before many times by Brian but I keep forgetting the reasoning.
Comment by DarkNova6 5 hours ago
Java always had a focus on enabling library authors over empowering call-site users. And I think approach has proven its dividend.
Comment by cogman10 3 days ago
If you were to call a function with a site declared value type, the function you call would have to have syntax as well to indicate "this is a value type I'm receiving and not a regular type" so that it doesn't do an illegal operation with the value type.
Comment by parallax_error 3 days ago
Comment by nogridbag 2 days ago
We have tons of wrapper classes in our codebase which seem ideal as value objects. For example, instead of passing around "String emailAddress", we have a wrapper class EmailAddress. While we can now make our EmailAddress class a value object, we cannot do "==". Unlike ZoneId in which there's a fixed set and you can intern all ZoneIds in the string pool, email addresses are unique and trying to intern all email addresses would be a bad idea. So it seems like using .equals is here to stay even for these types of basic classes.
Comment by cogman10 3 days ago
Comment by fallingbananna 3 days ago
As a layman I would assume nothing is preventing it, other than the large amount of effort it would take to implement.
Comment by DarkNova6 3 days ago
Comment by joe_mwangi 3 days ago
Comment by J-Kuhn 3 days ago
This makes it impossible to flatten - as the VM needs to know the total size when creating the memory layout for a class...
* (with a small exception: if the strings use different coders, one can be twice as long and the backing arrays would still have the same size)
Comment by DarkNova6 3 days ago
Comment by theanonymousone 3 days ago
Comment by HexDecOctBin 3 days ago
Comment by DarkNova6 3 days ago
Comment by spacechild1 3 days ago
Naturally, a language that properly supports value semantics does not have any of these limitations.
Comment by DarkNova6 3 days ago
This is the strength of Valhalla. Instead of a binary object/value choice you have different levels of semantic guarantees that will give you better performance characteristics, depending on what your type will be able to give up.
You can have tearability for a complex/quaternion number without problem. But if you have something such as a Range-type class, that gets more problematic because cross-field constraints can be violated.
Comment by Smaug123 3 days ago
Comment by pwagland 3 days ago
Basically JEP401 guarantees that just adding "value" to the class won't change behaviour, which also means that you are not allowed to see object tearing, which requires atomic operations on the field.
Relaxing that would break safe publication rules, at least for Java.
Comment by DarkNova6 3 days ago
This is the correct default as the vast majority of developers using value types will not be aware of tearability, being taught that "value types are safe for parallel programming" without knowing nuances.
Comment by joe_mwangi 3 days ago
Comment by DarkNova6 3 days ago
Comment by germandiago 3 days ago
Comment by alasr 3 days ago
> For the primitive types longer than 32 bits (long and double), it is not guaranteed that reads and writes from different threads (without suitable coordination) are atomic with respect to each other. The result is that, if accessed under data race, a long or double field or array component can be seen to “tear”, where a read might see the low 32 bits of one write, and the high 32 bits of another. (Declaring the containing field volatile is sufficient to restore atomicity, as is properly coordinating with locks or other concurrency control.)
... ...
[0] - https://openjdk.org/projects/valhalla/design-notes/state-of-...
Comment by jeroenhd 3 days ago
I don't really see why you would need specific hardware to express a type in a programming language, though.
Comment by jerven 3 days ago
Comment by muvlon 3 days ago
Probably still not widespread enough for OpenJDK to unconditionally assume it's supported, but I think we're getting there. (Is there "caniuse" for CPU features?)
Comment by Coelacanthus 1 day ago
Comment by DarkNova6 3 days ago
Comment by my-next-account 3 days ago
Comment by DarkNova6 3 days ago
Comment by HexDecOctBin 3 days ago
Comment by cogman10 3 days ago
It will likely require you poke in the `jdk.internal` package space.
Comment by pregnenolone 3 days ago
Comment by ligarota 3 days ago
This is it https://github.com/openjdk/jdk/commit/cc278dbb8a1ca0754d5842...
Comment by moritzwarhier 3 days ago
Comment by kuhsaft 3 days ago
Comment by whytevuhuni 3 days ago
Comment by pjmlp 3 days ago
There are other projects that depend having it in place, SIMD (vectors), reiffed generics, non nullable types.
Comment by z0ltan 3 days ago
Comment by hn4yci687u 3 days ago
Comment by nirvdrum 3 days ago