Java 23
openjdk.org
openjdk.org
https://openjdk.org/projects/jdk/23/
471 could be the harbinger of breaking changes if they further deprecate it by making you flip on a startup option in a later version, like the way JDK 9 broke a lot of stuff that still needs --add-exports or --add-opens to run. The winner in the "never gets out of the incubator" is 469 (the Vector API) which would be easy to blame on vector instructions being a perennially moving target but is really hung up on the (difficult) Project Valhalla -- however it's the kind of project you might have to finish quickly or never finish because every delay gives the targets another chance to move.
Most of the improvements are in existing preview areas.
This is following the semiannual release cadence.
I also really like relaxing constraints on when super() has to be called in constructors. It feels like language designers have been super super scared to let users do anything maybe possibly untoward, and have massively restricted constructors as a keep-you-all-safe-from-yourselves move. But it hampered hackability of inheritance, and makes a bunch of initialization tasks hard or frustrating to do, requires participation from your super. I'd love to see this shift come to other languages, of trying to do some relaxation of what can come before super(). https://openjdk.org/jeps/482
as being forced to call super() first thing in constructors was an annoyance to me when I was coding Java before JDK 1.0.
* https://www.oracle.com/java/technologies/java-se-support-roa...
Some of the proposals/in-progress features look very similar to features rolled out in C# over the last 3-4 years.
https://learn.microsoft.com/en-us/dotnet/csharp/language-ref...
https://timdeschryver.dev/blog/pattern-matching-examples-in-...
469: Vector API (Eighth Incubator)
https://devblogs.microsoft.com/dotnet/dotnet-8-hardware-intr...
473: Stream Gatherers (Second Preview)
I didn't read it closely enough to determine if this is more like LINQ over C#'s IEnumerable or more like System.Threading.Channels.
https://chrlschn.dev/blog/2023/10/dotnet-task-parallel-libra...
476: Module Import Declarations (Preview)
Some bits of this: https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/cs...
But probably more aligned with: https://learn.microsoft.com/en-us/nuget/consume-packages/pac...
477: Implicitly Declared Classes and Instance Main Methods (Third Preview)
https://learn.microsoft.com/en-us/dotnet/core/tutorials/top-...
480: Structured Concurrency (Third Preview)
Also feels like one aspect of System.Threading.Channels.
Other than that, note that Panama Vectors are in a rough shape at this moment. Perhaps more feedback from users will make the authors realize that both API and implementation are flawed and map poorly onto many critical parts of algorithm implementations, particularly shuffles. They require a lot more API changes and implementation work to catch up to the current state of Vector APIs in .NET.
Of course the reality of not having access to stack buffers and fast off-heap memory allocation/deallocation also hinders the usability.
In the end, both of these will be at least partially mitigated with pain of engineers who work on e.g. Lucene, but it's something that comes to C# "naturally" and is easily accessible to a developer that does not have as much prerequisite knowledge.
> Also add structured concurrency to the list - it's a subset of functionality that naturally arises Task composition and async/await since their introduction.
Could be wrong, but the JEP feels like it goes a bit deeper than `async/await` in C#. It feels like a hybrid between Task and Channels but operating at a lower level.Reading the proposal again, it's difficult to agree with this. The proposal talks about concepts that occur naturally when writing asynchronous code with C# (or F#, which has its own pros and cons, but mostly nicer combinators). For example:
Response handle() throws ExecutionException, InterruptedException {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Supplier<String> user = scope.fork(() -> findUser());
Supplier<Integer> order = scope.fork(() -> fetchOrder());
scope.join() // Join both subtasks
.throwIfFailed(); // ... and propagate errors
// Here, both subtasks have succeeded, so compose their results
return new Response(user.get(), order.get());
}
}
becomes the following in C# async Task<Response> Handle()
{
var user = FindUser();
var order = FindOrder();
return new(await user, await order);
}
For the happy path, the difference can be more drastic <T> List<T> runAll(List<Callable<T>> tasks)
throws InterruptedException, ExecutionException {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
List<? extends Supplier<T>> suppliers = tasks.stream().map(scope::fork).toList();
scope.join()
.throwIfFailed(); // Propagate exception if any subtask fails
// Here, all tasks have succeeded, so compose their results
return suppliers.stream().map(Supplier::get).toList();
}
turns into var results = await Task.WhenAll(tasks);
// Or, assuming .GetUser returns Task<GitHubUser>
var names = new[] { "java", "dotnet", "golang", "rust-lang", "python" };
var repos = await Task.WhenAll(
names.Select(n => GitHubApi.GetUser(n));
And so on and so forth. You can mix and match more complicated flows with CancellationToken and its CancellationTokenSource, TaskCompletionSource, Task.WhenAll/Any/Each (the last one transforms a collection of tasks into an asynchronous sequence of results). Apply exception handling at per-task or task collection level for additional customization. All of this occurs by composing the building blocks, rather than needing complex and bespoke API. void serve(ServerSocket serverSocket) throws IOException, InterruptedException {
try (var scope = new StructuredTaskScope<Void>()) {
try {
while (true) {
var socket = serverSocket.accept();
scope.fork(() -> handle(socket));
}
} finally {
// If there's been an error or we're interrupted, we stop accepting
scope.shutdown(); // Close all active connections
scope.join();
}
}
}
It's not exactly clear the exact specifics of how the above works regarding child tasks being able or unable to shutdown the parent scope - it only has the exception handler for the loop itself, which then performs .shutdown, which interrupts all the child tasks.However, if we continue with this assumption, then C# implementation is just the following:
async Task Serve(Socket socket) {
using var cts = new CancellationTokenSource();
try {
while (true) {
var conn = await socket.AcceptAsync(cts.Token);
// Or pass cts to Handle itself if you want to allow the handler to cancel the listener task.
_ = Task.Run(() => Handle(conn, cts.Token));
}
}
finally { cts.Cancel(); }
}
With that said, the difference to Java Structured Concurrency as proposed is that it offers tree-like tracking of child tasks/green threads, control over their execution, and, possibly, nicer debugging experience.I don't have an opinion whether the semantics of .shutdown() which ends up calling .interrupt() are sound without reading more carefully. If it can interrupt the execution at any point, this is extremely dangerous and will break a lot of existing code. It's one of the reasons why thread.Abort() got deprecated in .NET - the API brings too many issues and cannot be made to work reliably without hindering optimizations or violating expectations.
If it's not the case and it's more like flowing CancellationToken implicitly, then it may be a nicer experience depending on the situation. If it only interrupts upon suspending on .Join, IO wait or similar - it's still the kind of API that you are forced into instead of deciding whether cooperative cancellation is even safely possible given specific logic, without having to write try-catch blocks for each callsite. This is my surface impression and I will continue looking into this API.
I have been actively wanting this feature over half a decade. Over the same time, my javadocs are already written in markdown with sparse html smuggled in as strictly needed.
For example, instead of:
```
<p>Accepts these inputs:<br>
<ul>
<li>phone numbers</li>
<li>zip codes</li>
</ul>```
I have been writing:
```
Accepts these inputs: <br>
- phone numbers <br>
- zip codes <br>
```
My spiel is super well rehearsed to discuss why most projects should not write their javadocs in full html. Every project I have worked on over 2 decades has never actually rendered javadocs to the standalone HTML pages. Those projects are (i believe) the exception, things like library code. Thus, most developers read the documentation in the source code, not as rendered html. Caveat: Yes, some IDEs will render the html javadocs inline in source. I do think that has been a useful crutch. But, why force a developer the extra burden of writing html if nobody wants to read html?
Three cheers for markdown!! <3
You'll be missed by Java jokes everywhere
That was "edgy" and "innovative" at the time and people thought they were being cool... Thank you for everyone that worked on it, but we can all agree now with the benefit of hindsight that was overreaching.