Let’s start with the biggest announcement.

JDK 21 has moved into the Rampdown phase. In theory, this phase should mark the end of the changes. Although I was all the time aware of the enormity of OpenJDK 21, the final list of JEPs does not stop to further impress me.

Due to the number of new features, I can’t go into each one in depth. So I’ve decided to do a little experiment – I’m going to present each function in one sentence plus a code snippet (when suitable).

Therefore, without further ado, here we go:

In the case of ordered collections, a consistent interface has been introduced for retrieving the first and last elements, as well as reversing the sequence.

interface SequencedCollection extends Collection {
    // new method
    SequencedCollection reversed();
    // methods promoted from Deque
    void addFirst(E);
    void addLast(E);
    E getFirst();
    E getLast();
    E removeFirst();
    E removeLast();
}

As the likelihood of a GC needing to “clean up” a given object is reduced with its lifetime, having a different pipeline for short-lived and long-lived objects is standard in GCs and is now introduced to the ZGC.

The ability to easily destructure the Records, allowing specific fields to be extracted from them, and to be used in pattern matching (next JEP).

if (obj instanceof Point(int x, int y)) {
    System.out.println(x+y);
}

Ability to use switch for pattern matching, including advanced options.

static String formatterPatternSwitch(Object obj) {
    return switch (obj) {
        case Integer i -> String.format("int %d", i);
        case Long l    -> String.format("long %d", l);
        case Double d  -> String.format("double %f", d);
        case String s  -> String.format("String %s", s);
        default        -> obj.toString();
    };
}

Introducing into the JVM the concept of threads managed not by the operating system, but by the virtual machine itself.

Thread.builder().virtual().factory();

Goodnight, Sweet Prince

In order to make Java more secure, the developers plan to prohibit the loading of certain categories of Virtual Machine Agents in the future without a special flag.

Introducing a standard API into Java that allows the use of a technique known as KEM, used in quantum cryptography algorithms, among others.

The ability to create templated blocks of text in Java, giving much more power and security than string interpolation.

JSONObject doc = JSON_VALIDATE."""
        {
            "name":    {name},
            "phone":   {phone},
            "address": {address}
        };
        """;

A change to the Launch Protocol in Java, allowing heavily simplified Java classes to be written, mainly for educational purposes.

void main() {
    System.out.println("Hello, World!");
}

Introducing typed interoperability with applications written in C (and in future other compiled languages), as well as native operating system memory.

Introduction to Java _ wildcard, used when we do not want to define a specific expected value/type in pattern matching, and also as a hint to linterers when we know that a declared variable is unnecessary.

if(r instanceof Point(_, int y))

A set of structures to manage threads (and not only virtual ones) – especially useful for error and cancelation handling.

 try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
        Supplier  user  = scope.fork(() -> findUser());
        Supplier order = scope.fork(() ->fetchOrder());

        scope.join().throwIfFailed();

        return new Response(user.get(), order.get());
    }

An alternative to ThreadLocal, designed primarily with virtual threads in mind.

final static ScopedValue<...> V = ScopedValue.newInstance();

ScopedValue.where(V, ).run(() -> {  V.get() });

The API enabling vector operations, made available by modern processors, waiting for Valhalla Project.

And finally, if that’s not enough for you, I have two videos for you.

One is a publication from the official Java channel, in which Nicolai Parlog in a 20-minute video describes each of the JEPs in more detail:

The second is an opportunity for me to smuggle in one of my favorite programming channels on YouTube. If you appreciate light-hearted, ‘memetic’ studies, then Fireship should definitely appeal to you. The content of this channel is primarily short, under five-minute videos.

The author focuses on the most relevant topics from the world of programming. For me, this is always a fantastic way to keep up to date with news from areas outside my main specialization, such as new programming languages or innovations like vector databases.

I emphasize this because a video dedicated to JDK 21 was recently released on the Fireship channel, and I encourage you to see if this format suits you too. In my opinion, it is worth giving it a chance:

PS: I don’t know if you remember, but the “no changes” has been slightly broken in the past. O the occasion of JDK 20, the developers forgot to throw in the Vector API incubation, which was added basically right before the release – however, without any changes in comparison to JDK 19

For the sake of momentum, we are now changing pace and instead of briefly discussing the upcoming version, scheduled for release in the autumn, we will talk more broadly about what has been announced in the context of Valhalla. The release date is not yet known, but further documents suggest that we are getting closer to the final release. Brian Goetz has shared a Design document on nullability and value types that summarises a new phase of the project’s development. Again, we can look at the challenges, but also the new opportunities that are emerging – Valhalla seems to be a project being developed in a strongly experimental way.

There is a lot to learn from Java when it comes to truly iterative project development.

If you haven’t been keeping up to date with Valhalla’s development, you may have missed the fact that at one point the developers proposed the .val and .ref suffixes. These were supposed to tell you whether you wanted to use an object as a value or as a reference. This was the most problematic of the proposed changes for me, as I was worried about complicating the syntax. For the time being, it looks like it will be possible to dispense with them. Development work throughout the project has reduced the differences between primitive and objects to two fundamental differences – having a default value (like 0 for int) and support for nullability. In the next iteration of work on Valhalla, the former was addressed through the concept of implicit constructors for Value Class.

value class Complex {
     private int re;
     private int im;

     public implicit Complex();
     public Complex(int re, int im) { ... }

     ...
}

Regarding nullability, there is a proposal to introduce two additional markers when defining a type – ! meaning we do not allow nulls and ? meaning this object is nullable. In brief:

  • Foo? means this type contains null in its value set.

  • Foo! means this type does not contain null in its values set.

  • Foo means 🤷‍♂️ – in other words, the undefined state of nullability

Over time, developers will aim for the version without the appropriate annotation to take on the characteristics of annotated variants – at this point, it seems that unmarked Foo will be treated as non-nullable in most cases.

Unfortunately, verification of the above will require a mixture of compile-time and runtime checks, and will also be very difficult and limited – at least at the beginning. Adding new features to an existing language is never straightforward, and the detailed solutions proposed by Brian show how many cases need to be handled. As proof of this, the day after the Design document on nullability and value types was made available, a Briefest summary of today’s Valhalla+nullness picture, which is a distillation of the document, hit the mailing list. Although it is a very good summary, to really understand the essence of the changes I had to review the original text. The map, unfortunately, is not the territory.

I do not overestimate my place in value chain.

Guava’s glory days are probably behind it. Much of the functionality it brought – revolutionary in its time – has either been incorporated into the JDK or superseded by specialized projects. Guava admittedly still has a few unique tricks, such as Multimap and unique implementations of other collections or graph structures, and auxiliary structures for handling streams. This and other additions make it still an important player in the ecosystem, just no longer as ‘default’ as it was at the JDK 1.7 level. The project is still under development, and recently saw the version of Guava 32.0.

This one, in turn, means a big clean-up – the @Beta annotation has been removed from almost all classes, effectively meaning a major stabilisation of many APIs. The purpose of the @Beta annotation was to allow users to provide feedback before the API was fully stabilised, now the developers have decided to clean up the project a bit. Therefore, the new Guava release should be seen as an inventory to facilitate future work and not introduce any big changes. However, I decided to mention it, because people who were afraid of unstable APIs will be able to use a large set of stabilized methods with a clear conscience. It is also worth mentioning that the developers managed to close a certain vulnerability, unfortunately at the cost of some file-handling issues on Windows. If you are using that system, you should jump straight to version 32.0.1, containing the fix.

Note: Scala has undergone significant changes in its release cycle.

Scala 3.3.x is the first in a series of Long Term Support (LTS) releases that will be actively maintained for at least three years. Analogous to the Java release model, Scala will have smaller releases (3.4, 3.5, etc.), called Scala Next. Bug fixes and improvements from these releases will be carried back and released as 3.3.x patches, but the LTS will not include changes to the API. This solution is intended to increase convenience for library developers, who will receive regular bug fixes without forcing users to update their compiler version.

Scala 3.3.0 itself reintroduces linting into the Scala compiler, which enables the detection of unused symbols and values, with the announcement of additional options in the future. A more consistent syntax without curly brackets has also been introduced for some use cases. This allows brackets around method parameters to be replaced by a colon, leading to cleaner, shorter, and often more readable code.

Two new methods have also been added to the standard library, boundary and break, which allows a safer and more expressive return of values from blocks. This makes it possible to indicate that the output from a function can also occur within one of the lambdas, what the developers call a ‘quick escape’.

def sumOfRoots(numbers: List[Double]): Option[Double] = boundary:
  val roots = numbers.map: n =>
    println(s" * calculating square root for $n*")
    if n >= 0 then Math.sqrt(n) else break(None)
  Some(roots.sum)

Finally, I’d like to mention the release of Hibernate Reactive 2.0. At first glance, it appears to be a fairly modest release – in the changelog, we only see compatibility with Hibernate ORM 6.2.4 and Vert.x SQL Client 4.4. Nevertheless, don’t be fooled by appearances – on closer inspection, you’ll find that this is a colossal leap.

Indeed, Hibernate Reactive 1.0 was only compatible with the Hibernate 5.x line. The changes introduced by Hibernate 6.0, released about a year ago and further improved in subsequent releases, are so significant that the developers encourage you to read the migration guide.

Read More