19 ms·
Java 21: The Nice, the Meh, and the Momentous
- ecshafer 3y agoJava getting better pattern matching is a great change. Id really like more of the functional features to make it into Java. I would love if Java pattern matching could at least get to the level of ruby pattern matching. Ruby pattern matching will allow you to deconstruct arrays and hashes to get pretty complicated patterns, which is really powerful. Right now it seems like Java might have that with a lambda in the pattern, but its not going to be as elegant as ruby where: case {name: 'John', friends: [{name: 'Jane'}, {name: 'Rajesh'}]} in name:, friends: [{name: first_friend}, *] "matched: #{first_friend}" else "not matched" end #=> "matched: Jane" But the big change here is virtual threads which should be a game changer.
- unregistereddev 3y agoPattern matching is a neat tool to keep in the toolbox. When it's the right tool for the job, it is really cool and is a lot cleaner than a bunch of conditional checks. However, I rarely reach for it. Maybe my use cases are unusual? I am genuinely curious how often other developers find pattern matching to be the best tool for the job.
- grumpyprole 3y agoOne example for you: anytime you needed to use the "Visitor pattern" to do a transformation from one representation to another - you don't need it now. Sealed classes and pattern matching will be more succinct and easier to reason about.
- bcrosby95 3y agoIt probably depends on the language you're using. Pattern matching is awesome in Erlang and Elixir. In most other languages it ranges from "nice" to "bleh".
- nayuki 3y agoPattern matching is awesome in Rust. It carries the stellar legacy of Haskell.
- Jtsummers 3y agoWhen available, I pretty much always use pattern matching. It tends to shorten code while not reducing clarity (often increasing it) which means fewer opportunities for errors to creep in. Statically typed languages that can detect incomplete case handling also reduces the chances for some errors (as long as you don't make a catch-all case) but also helps when you change something so that a new case is needed. It also tends to shift the code to the left, reducing the indentation. So shorter, clearer, less unnecessary indentation. Generally a positive.
- weatherlight 3y agoIn languages that have strong support for pattern matching, whether it be on values or types, I find myself reaching for it instead of conditionals. It's all about the explicitness for me. You have to list out all the cases you care about, so there's no room for ambiguity. Plus, the compiler will usually warn you if you've missed a case, which is like a built-in bug catcher. It's also great for working with immutable data, less state to worry about. And let's talk about readability; the code basically documents itself because you can see the shape of the data right in front of you. You can even destructure data on the fly, pulling out exactly what you need. If you're using a statically-typed language, pattern matching adds an extra layer of type safety. And, not to forget, it nudges you toward a more functional style of coding, which I find leads to cleaner, more modular code. So yeah, I reach for pattern matching quite a bit; it often feels like the right tool for the job.
- ecshafer 3y agoI think that you can replace almost any If else with pattern matching. Pattern matching makes type checks easier, which if you are really heavily using types through your program, makes pattern matching even better.
- hibikir 3y agoPattern matching is what makes sum types ergonomic enough to be used. Many a Java design doesn't use said interface-based sum types because it's so cumbersome to use them. But whena language has pattern matching, then suddenly designing with sum types in mind is done a lot, and therefore you see examples of good pattern matching everywhere. When I teach Scala, a very high percentage of the teaching time is ultimately down to re-introducing how to design business domains, because seasoned devs just reach for large classes with a million optional fields, which not only can represent valid systems states, but thousands of invalid ones.
- owlstuffing 3y agoPatterns are somewhat nice to have, but for me they’re difficult to read, and not because my brain isn’t used to them. The simple identifier instanceof is about all I’ll use _most_ of the time. Otherwise, yes they are more concise, but lose too much information in the process. I’d rather see a boatload load of other features before patterns. I’ve been experimenting with project manifold[1]. _That_ is the path Java sb on. Just my take. 1. https://github.com/manifold-systems/manifold https://github.com/manifold-systems/manifold
- frou_dh 3y agoI really like that Ruby throws NoMatchingPatternError if none of the patterns match. It's a bit like the much-acclaimed exhaustive pattern matching in static languages (though at runtime rather than compile-time, obviously) and better than just silently falling off the end, which IIRC is what Python's pattern matching does.
- rusk 3y agoIn Python you can terminate a for loop with else, which will be run whenever the loop runs to the end without breaking
- specialist 3y agoNeat. Will check it out. I recently spotted a (new to me) foreach / else construct in a templating language (sorry, forget which one); else is invoked if the list is empty. Nice sugar for common outputs like "no items found". I appreciate modest syntactic sugar. For instance, my #1 sugar wish is for Java's foreach is to do nothing when the list reference is null. Versus tossing a NPE. Eliminates an unnecessary null check and makes the world a little bit more null-safe.
- rusk 3y ago> else is invoked if the list is empty. for / else should do that too …
- frou_dh 3y agoThat's not particularly relevant to the nice pattern matching property I mentioned. If you need to manually write supplementary code to get the exhaustiveness safety then that's back into the realm of bog-standard defensive programming. Here's what I mean. The Ruby will throw NoMatchingPatternError and the Python will silently do nothing. x = [10, "figs"] case x in [n, "apples"] :foo in [n, "oranges"] :bar end # --- x = [10, "figs"] match x: case [n, "apples"]: ... case [n, "oranges"]: ...
- brightball 3y agoSimple solution: JRuby. Virtual threads are going to make Ruby fibers work properly for JRuby so that’s going to be huge as well. Charles Nutter gave an update in August. 45 minute mark he talks about virtual threads. https://youtu.be/pzm6I4liJlg?si=vKVICrola4OmJIal https://youtu.be/pzm6I4liJlg?si=vKVICrola4OmJIal
- munificent 3y agoWe recently added pattern matching to Dart [1], so I'm always keen to see how it compares to similar features in other languages. In case it's interesting, here's that Ruby example ported to Dart: print(switch ({'name': 'John', 'friends': [{'name': 'Jane'}, {'name': 'Rajesh'}]}) { {'friends': [{'name': var firstFriend}, ...]} => "matched: $firstFriend", _ => "not matched" }); Pretty similar! The main differences are that Dart doesn't have symbols, so the keys are string literals instead. Also, variable bindings in patterns are explicit (using "var") here to disambiguate them from named constant patterns. [1]: https://medium.com/dartlang/announcing-dart-3-53f065a10635 https://medium.com/dartlang/announcing-dart-3-53f065a10635
- brabel 3y ago> We recently added pattern matching to Dart [1] I've been using that and I love it, in general... but can I ask you why do we need to name a variable in a pattern like this: switch (p) { Person(name: var name) => ... } That's the only thing that feels a bit annoying as you have to rename the variable... In Java, this would be something like: Person(var name) -> ... EDIT: I guess it's to support `Person(name: 'literal')` matches. > Dart doesn't have symbols That's weird, as I actually use sometimes `#sym` (which has type `Symbol`)?? print((#sym).runtimeType); This prints `Symbol` :) I know you know Dart in and out, but could you explain why this is not actually a symbol in the way Ruby symbols are?
- munificent 3y agoWe require "var" before variable patterns because we also allow named constants in patterns (which match if the value is equal to the constant's value): const pi = 3.14; // Close enough. switch (value) { (pi, var pi) => ... } This case matches a record whose first field is equal to 3.14 and binds the second field to a new variable named "pi". Of course, in practice, you wouldn't actually shadow a constant like this, but we didn't want pattern syntax to require name resolution to be unambiguous, so in contexts where a constant pattern is allowed, we require you to write "var", "final", or a type to indicate when you want to declare a variable. Swift's pattern syntax works pretty much the same way. > > Dart doesn't have symbols > That's weird, as I actually use sometimes `#sym` (which has type `Symbol`)?? Oh, right. I always forget about those. Yes, technically we have symbols, but they are virtually unused and are a mostly pointless wart on the language. It's not idiomatic to use them like it is in Ruby.
- Vicinity9635 3y ago[flagged]
- adra 3y agoVirtual threads are going to be great, but they're still limited (still starved the pool when used with 'synchronized' blocks), and they aren't the structured concurrency power houses like kotlin coroutines, but its an invaluable tool that will only continue to accelerate as the ecosystem moves to adopt them. Expect a lot of libraries to start release versions that are java 21 baseline because of this feature alone. We're in for a little bit of dependency hell for the short while. Thankfully, devs have been exposed to a mostly final loom for a year, so my hope is that at least the big projects are well on their way to quick adoptions. Unlike the 8->11 migration which largely brought pain, the 8->21 release brings with it a ton of value that i think will encourage most shops to actually pull the trigger and finally abandon 8.
- tantamounta 3y agoWith the API being nearly the same, I keep just thinking that Virtual Threads are basically identical to Platform Threads except that they use far less memory (so you can have lots more of them). Are there any other actual differences? Better Peformance?
- noelwelsh 3y agoThe context switch time is much smaller, so yes, better performance.
- pron 3y agoThe relationship between throughput, latency, and concurrency in servers is expressed via Little's theorem. If your server is written in the thread-per-request style -- the only style for which the platform offers built-in language, VM, and tooling support -- then the most important factor affecting maximum throughput is the number of threads you can have (until, of course, the hardware is fully utilised). Being able to support many threads is the most effective improvement to server throughput you can offer. See: Why User-Mode Threads Are Good for Performance https://youtu.be/07V08SB1l8c https://youtu.be/07V08SB1l8c
- tantamounta 3y ago
- marginalia_nu 3y ago> Miscellaneous new methods -- meh Dunno, several of these are tangible QoL boosts: Math.clamp(), List.reversed(), List.addFirst(), List.addLast(), Character.isEmoji()
- bcrosby95 3y ago> List.reversed(), List.addFirst(), List.addLast() These fall under sequenced collections, not "miscellaneous new methods".
- marginalia_nu 3y agoI guess? I found them under the API diff linked as "miscellaneous new features".
- winrid 3y agoSo I can reverse a list without using "streams" now? Thank heavens
- baq 3y ago> "Hello, World!".splitWithDelimiters > ("\\pP\\s\*", -1) > // ["Hello", ", ", "World", "!", ""] > Meh My brain just melted.
- hinkley 3y agoI'd have a lot of uses for that. But also worry about it enabling more stringly-typed code.
- mrkeen 3y agoIn the code example for virtual threads, I have no idea what will happen in parallel. How do I reason about the order in which the calls change the state of the world?
- Jtsummers 3y agoThat's all sequential code, it would be run inside a single "virtual thread". Note that the async code on the right is also sequential, just structured through an async API.
- Svenskunganka 3y agoFrom my perspective they're not entirely equivalent. The async variant seems to be batching getImages and saveImages, while the sync variant gets and saves each image individually, sequentially.
- Jtsummers 3y agoThey aren't perfectly equivalent because the virtual thread example uses a loop instead of the following (dropping the try/catch): // client.sendAsync(request, HttpResponse.BodyHandlers.ofString()) var response = client.send(request, HttpResponse.BodyHandlers.ofString()); // .thenApply(HttpResponse::body) var body = response.body(); // .thenApply(this::getImageURLs) var urls = getImageURLs(body); // .thenCompose(this::getImages) var images = getImages(urls); // .thenAccept(this::saveImages) saveImages(images); And if it had been written this way it would have been clearer that they are, in fact, equivalent. But generally people don't write like this, they use looping constructs. Regardless, the important bit is that the parallel/concurrent bit of the async one is that it is cast off into an async system. The following execution steps are, well, steps. Each executed in sequence. Just like the body of the virtual thread example would be executed, but without the cumbersome noise of thenApply and thenCompose and such.
- Someone1234 3y agoIf you're viewing that website on a desktop, I strongly suggest removing max-width: 90ch from the body css. Instead of 50% white space, it goes full width and makes the table substantially more readable (particularly the code samples).
- munk-a 3y agoHilariously enough I was initially confused by this comment because the webpage rendered so readably for me - the base CSS is actually quite reasonable and because I have JS disabled by default the page never re-rendered into the thinner mode.
- Someone1234 3y agoIt may be my specific setup. But on a 1440p display, 125% OS scale, I'm seeing more white left/right than actual content in the middle. It is also wrapping the code making it difficult to read. Completely readable at 100% width though.
- pacoverdi 3y agoI viewed it on Firefox for Android and I immediately had to jump to reader mode for the same reason. But I tend to use reader mode on most sites anyway because it's an easy way to get rid of banners (cookies, subscription etc.)
- waynesonfire 3y ago> Over 10,000 bug fixes Most of which were likely introduced during new feature development in recent releases. To suggest that this on its own somehow manifests a more stable jdk compared to some ancient, battle tested version of the jdk is debatable. I find it rather concerning that so many bugs exist to begin with. Why are these not caught sooner? Has the whole world gone crazy? Am I the only one around here who gives a shit about quality? Mark it zero!
- specialist 3y agoRandomly looking at bugs fixed the last 10 weeks, it seems like a healthy mix of old and new bugs. https://bugs.openjdk.org/browse/JDK-8316305?filter=-7&jql=project%20%3D%20JDK%20AND%20issuetype%20%3D%20Bug%20AND%20status%20in%20(Resolved%2C%20Closed)%20AND%20resolved%20%3E%3D%20-8w%20ORDER%20BY%20updated%20DESC https://bugs.openjdk.org/browse/JDK-8316305?filter=-7&jql=pr... Being allergic to JIRA, my JIRA-fu is weak, so there's probably an easier/faster way to report bugs fixed in v21. Any way. > Am I the only one around here who gives a shit about quality? Ages ago, I was a QA/Test manager. So I appreciate your sentiment. But it seems to me that Oracle's being a FANTASTIC shepherd of Java. Definitely a huge upgrade, at the very least.
- doodpants 3y agoYou might be the only person in the world who writes bug-free code on the first try.
- rr808 3y agoJava has been around for nearly 30 years, I'd hope the core libraries had very few bugs by now.
- pron 3y agoWhile you're right that the number of bugs is not very meaningful and most are probably work on brand new features, but bugs in old features are always first fixed in the current version, and then only a subset of them (usually a small subset) is backported to old releases, and regressions are not common. As to why some bugs go unnoticed for long, if you look at the bug database for reports of bugs that have been effect for a long while you'll see that these are almost always rather extreme corner cases (or, more precisely, the more utilised a mechanism is, the more extreme would be its old bugs). That's simply because full coverage is simply infeasible for software of such size (~8MLOC); you see similar bug numbers for the Linux kernel. The largest software that can be shown to be free of bugs is currently on the order of 10KLOC, so if your software is much larger than that and isn't getting many bug reports it's probably because it's not used that much.
- billfruit 3y agoDoes it add stdint style names for integer types, unsigned integer types etc?
- layer8 3y agoThe size of the integer types are already fixed by the JVM specification (int is always 32 bits, etc.), and there are no unsigned integer types in Java except for char (a 16-bit unsigned integer type). Furthermore, Java does not support alias names for types. Hence it’s unclear what your question is aiming at.
- szatkus 3y agoAFAIK Java 8 added a few methods that helps you handle integers as if they were unsigned, like `toUnsignedString`. I think it's enough for any exotic cases.
- PaulHoule 3y ago(1) It's a bit of a bad smell (which he points out) that records aren't being used much at all in the Java stdlib, I wrote something that built out stubs for the 17 and 18 stdlibs and that stood out like a sore thumb. I do like using records though. (2) I've looked at other ways to extend the collections API and related things, see https://github.com/paulhoule/pidove https://github.com/paulhoule/pidove and I think the sequenced collections could have been done better. (3) Virtual Threads are kinda cool but overrated. Real Threads in Java are already one of the wonders of the web and perform really well for most applications. The cases where Virtual Threads are really a win will be unusual but probably important for somebody. It's a good thing it sticks to the threads API as well as it did because I know in the next five years I'm going to find some case where somebody used Virtual Threads because they thought it was cool and I'll have to switch to Real Threads but won't have a hard time doing so.
- papercrane 3y agoI suspect if we had records from the start they'd be all over the stdlib, but because of backwards compatibility they'll likely only be considered for new APIs.
- twic 3y agoI think the biggest impact of virtual threads is that the ecosystem will abandon asynchronous APIs. No more futures, callbacks, servers where you have to make sure not to block the thread, reactive frameworks, etc. Just nice simple imperative blocking code. Nima is the first example i've seen: https://helidon.io/nima https://helidon.io/nima We've had two production bugs in the last two weeks caused by handlers blocking the server thread in apps using an async web framework, which would simply not have happened with a synchronous server.
- Vicinity9635 3y agoThe examples having to word wrap in a tiny text box look even more absurd and unreadable when the page is only using 1/3rd of the screen. What is with this awful formatting? https://i.imgur.com/nQmt7Qo.png https://i.imgur.com/nQmt7Qo.png
- BrianKamrany 3y agoIt is easier to read something that is page-sized, as opposed to taking up the full screen. Although it does look weird.
- hinkley 3y agoWhat's the Scala community think about this development? I would think this would affect them quite a lot. Google is not helping.
- dionian 3y agoIt's great, but irrelevant since Scala is already so far ahead. I will start to care if i am ever forced to do java again. I love how much better Java is getting! Most of these things we have had in scala for a long time already, and much better versions.
- discodachshund 3y agoThe Typelevel folks on Discord are of the opinion it's not of much interest to them
- hinkley 3y agoI wonder if that's "not interesting" or "we already fixed this another way"
- rr808 3y agoScala community always thinks they're the best tool. The size of the community is at best static though, Kotlin and re-energized Java took away most of the reasons for using it. I know in my company the teams that went the Scala route complain of huge compile times and really struggle to find people, I think we'll probably port back to Java.
- vips7L 3y agoScala is just too complex, the tooling too slow, and the community had way too many breaking changes.
- hinkley 3y agoI went through a Scala book with a reading group. Lots of incredulity all around. Much more than the Java concurrency book, which wasn't easy either. An 'academic' language if ever there was one. But I recall it as the first vaguely Erlang-like language on the JVM, so whenever something about threading comes up I recall it. I'm learning Elixir instead.
- logicchains 3y agoDoes anyone know if Java virtual threads will also have channels and a select concept, like in Go?
- aardvark179 3y agoI think at some point yes. We certainly discussed it but it’s one of those things that takes time to really get right and performant.
- kaba0 3y agoJava already has many more concurrent and parallel data structures, so while it likely won’t have a keyword, it can definitely do it already.
- shaunxcode 3y agoJust spit balling but you should be able to use clojure core async channels and the blocking put/take/alts functions. Would probably take a small amount of work to expose those things to Java in an idiomatic way but should be doable. Please take all of that with a giant grain of salt though!
- anonymousDan 3y agoCan anyone explain this comment: "In the past, a thread pool didn't just throttle the incoming requests but also the concurrent resources that your app consumed. If you now accept many more incoming requests, you may need other ways to manage resource consumption."
- yCombLinks 3y agoYeah, if your server maxed out at 256 system threads you didn't have to worry about the fact that 1024 simultaneous calls would crash your DB. But now you're not limited by system threads
- YeBanKo 3y agoYou can still use connection pool + platform threads. Or executor with virtual threads and semaphores or blocking queues. It’s mostly a concern for someone who implements a connection pool, for most devs it’s gonna be the same config option of max connection, that you need to pay attention to. Any modern web app already has multiple instances of the app querying a db, so you have to keep a tally of total connection number either way.
- deleted 3y ago[deleted]
- hoistbypetard 3y agoIt sounds like it has some neat new features. But I'll never know because I'm never again going to use another Oracle thing. There's not a thing they could make that's good enough for me to agree to one of their EULAs and install it. Their behavior in that area is just staggeringly bad.
- matt_heimer 3y agoThen use Java without agreeing to an Oracle EULA. You can get a GPLv2 open source build from https://jdk.java.net/21/ https://jdk.java.net/21/ If you don't trust the Oracle based open source builds then just wait a bit for Microsoft, Redhat, and others to release their version 21 OpenJDK builds that will be found under https://adoptium.net/marketplace/ https://adoptium.net/marketplace/
- kaba0 3y agoNot this bullshit again.
- aggregat 3y agoWe have 2.1 million LOC in Java and we're moving to Java 21 (from 17) in two weeks when we branch for release. We have a hundreds of third party dependencies across the code base, a lot of the big ones (Hibernate, Spring, a lot of Apache). We write a big web application and maintain a big legacy desktop application in Swing. We run a dedicated nightly CI job that is on the latest Java release to get early warning for any incompatibilities. After the painful migration from 8 to 9 so many years ago it has been smooth sailing. In all those version upgrades over all those years and dozens of on premise installations with big customers we have never had a regression or a problem that was caused by the runtime itself.
- sylware 3y agoI am looking for assembly implemented JVMs (x86_64/risc-v/etc), that to remove SDK pressure and give stable auditability of machine code. Do those exist?
- ivanjermakov 3y agoI love pattern matching, but without a proper support for variant types it won't be as useful as it could. I'm aware of `permits` clause, but it's not good enough.
- bullen 3y agoSo it's just Thread.startVirtualThread(runnable); that's it? Going to be interesting!