Java 22: what’s new?

Java 22: what’s new?

Now that Java 22 is features complete (Rampdown Phase One at the day of writing), it’s time to walk through all the functionalities that bring to us, developers, this new version.

This article is part of a series on what’s new on the last versions of Java, for those who wants to read the others, here are the links: Java 21, Java 20, Java 19, Java 18, Java 17, Java 16, Java 15, Java 14, Java 13, Java 12, Java 11Java 10, and Java 9.

JEP 461 – Stream Gatherers (Preview)

Enhances the Stream API with support for custom intermediate operations. This is a preview API.

The Stream API provides a fixed set of intermediate and terminal operations. It allows terminal operations to be extended via the Stream::collect(Collector) method, but does not allow intermediate operations to be extended. Some operations are missing or others are possible via a set of operations or via an operation that doesn’t fully match what’s needed.

Over the years, many new terminal operations have been proposed, but even if most of them make sense, it’s not possible to add them all to the SDK. Adding the possibility of defining your own intermediate operations alleviates the problem.

With JEP 461, it is now possible to define your own intermediate operations via Stream::gather(Gatherer).

A gatherer represents the transformation of an element in a Stream into one-to-one, one-to-many, many-to-one or many-to-many and can stop the transformation if necessary, stopping the emission of element in the downstream stream.

Gatherers can be combined: stream.gather(...).gather(...).collect(...).

The java.util.stream.Gatherer interface defines the following methods:

  • initializer(): optional, can be used to maintain a state when processing elements.
  • integrator(): used to integrate a new element from the incoming stream, and if necessary emit an element in the downstream stream.
  • combiner(): optional, can be used to evaluate the gatherer in parallel for parallel streams.
  • finisher(): optional, called when the stream has no more input elements.

The Stream API has been enhanced with the following gatherers:

  • fold: stateful many-to-one gatherer that builds an aggregate incrementally and emits this aggregate when no more input elements exist.
  • mapConcurrent: stateful one-to-one gatherer that concurrently invokes a provided function for each input element, up to a provided limit.
  • scan: stateful one-to-one gatherer that applies a provided function to the current state and the current element to produce an output element.
  • windowFixed: stateful many-to-many gathere that groups input items into lists of a supplied size, outputting windows when full.
  • windowSliding: stateful many-to-many gatherer that groups input items into lists of a supplied size. After the first window, each subsequent window is created from a copy of its predecessor by deleting the first element and adding the next element from the input stream.

Here’s an example of using a gatherer provided in the JDK :

var numbers = List.of(1, 2, 3, 4, 5);

var slidingWindows = numbers.stream()
    .gather(Gatherers.windowSliding(3))
    .toList();

System.out.println(slidingWindows);
// [[1, 2, 3], [2, 3, 4], [3, 4, 5]]

The JavaDoc gives the following example of a gatherer that reproduces the Stream.map() operation:

public <T, R> Gatherer<T, ?, R> map(Function<? super T, ? extends R> mapper) {
    return Gatherer.of(
        (unused, element, downstream) -> // integrator
            downstream.push(mapper.apply(element))
    );
}

More information in the JEP 461

JEP 458 – Launch Multi-File Source-Code Programs

Since Java 11, it is possible to launch a program from a .java source file without first compiling it. The Java launcher will then compile the program in memory automatically before execution.

With JEP 458, it is now possible to launch a program from a source file that uses a class defined in another source file, this second file will also be automatically compiled in memory. Source files are searched in the usual Java directory hierarchy, which reflects the package structure.

Only source files used by the main program are compiled in memory.

More information in the JEP 458

JEP 447 – Statements before super (preview)

When a class extends another class and wants to call the parent class’s constructor in its own constructor, the JVM forces the call to the parent constructor to be the first instruction in the parent class’s constructor. This ensures that all fields in the parent class are initialized before the child class is built.

JEP 447 is a preview feature that allows instructions before calling the parent constructor as long as they do not access the instance being created.

Several examples are given in the JEP: parameter validation, argument pre-calculation, etc…

Here’s an example of parameter validation prior to JEP 447:

public class PositiveBigInteger extends BigInteger {

    public PositiveBigInteger(long value) {
        super(value);               // Potentially unnecessary work
        if (value <= 0)
            throw new IllegalArgumentException("non-positive value");
    }

}

And with the JEP 447 :

public class PositiveBigInteger extends BigInteger {

    public PositiveBigInteger(long value) {
        if (value <= 0)
            throw new IllegalArgumentException("non-positive value");
        super(value);
    }

}

The code is more readable and potentially avoids the effects of the parent constructor.

More information in the JEP 447

457 – Class-File API (Preview)

JEP 457 provides a standard API for parsing, generating and transforming Java class files. This API is in preview.

The Java ecosystem has numerous libraries for parsing and generating Java class files: ASM, BCEL, Javassist, … Most bytecode-generating frameworks use them.

The Java class format evolves every 6 months, with each new Java release, so the generation frameworks must evolve at the same time, at the risk of not supporting the latest language evolutions.

The JDK itself uses ASM to implement some of its tools, as well as for lambda implementation, which creates a discrepancy between the functionalities of one version of Java and what can be used within the JVM in the portions requiring class file generation, as you have to wait for a version of ASM supporting the new functionalities of version N before using them in version N+1.

The Class-File API overcomes this problem by providing an API within the JDK for parsing, generating and transforming class files.

The following presentation by Brian Goetz at the VM Language Summit 2023 describe the API, its design and use: A Classfile API for the JDK.

More information in the JEP 457

ListFormat

ListFormat is a new formatter that allows you to format a list of strings regarding a locale based on the Unicode standard.

Example :

var list = List.of("Black", "White", "Red");
var formatter = ListFormat.getInstance();
System.out.println(formatter.format(list));
// [Black, White, Red]

When creating the formatter we can pass it:

  • A locale, otherwise the default locale will be used.
  • The enumeration type: STANDARD, OR or UNIT. Default STANDARD.
  • Enumeration style: FULL, SHORT or NARROW. Default FULL.

The following table shows the different outputs for the US locale:

FULL SHORT NARROW
STANDARD Black, White, and Red Black, White, & Red Black, White, Red
OR Black, White, or Red Black, White, or Red Black, White, or Red
UNIT Black, White, Red Black, White, Red Black White Red

More information in the issue JDK-8041488.

Features coming out of preview

The following features comes out of preview (or incubator module) are now standard features:

  • JEP 454Foreign Function & Memory API: API for interconnecting the JVM with native code.
  • JEP 456Unnamed Variables & Patterns: allows you to use _ as an unnamed pattern or variable.

For details on these, please refer to my previous articles.

Nevertheless, an important change has been made in the Foreign Function & Memory API that should be noted: the introduction of the notion of restricted method. Some methods in this new API are marked as restricted: to use them, you’ll need to use the --enable-native-access=module-name command-line option. Currently, access to restricted methods generates a warning, but access to them may be forbidden in a future version of the JVM. Restricted methods are used to bind a native function and/or native data, which is inherently unsafe. For this reason, access to them must be given specifically via a command-line option.

Features that remain in preview

The following features remain in preview (or in the incubator module).

  • JEP-460Vector API: seventh incubation, API for expressing vector calculations that compile at runtime into vector instructions for supported CPU architectures. This new version includes bugfixes and performance enhancements.
  • JEP 464Scoped Values: second preview, enable the sharing of immutable data within and between threads. No noticeable changes in this new preview.
  • JEP 462Structured Concurrency: second preview, a new API that simplifies the writing of multi-threaded code by allowing multiple concurrent tasks to be treated as a single processing unit. No noticeable changes in this new preview.
  • JEP 463Implicitly Declared Classes and Instance Main Methods: second preview, simplifies the writing of simple programs by allowing them to be defined in an implicit class (without declaration) and in an instance method void main().
  • JEP 459String Templates: Second preview, a string template is a literal of String that lets you incorporate expressions and variables. No noticeable changes for this new preview.

For details on these, please refer to my previous articles.

Miscellaneous

Various additions to the JDK:

  • Console.isTerminal(): returns true if the console instance is a terminal.
  • Class.forPrimitiveName(String): returns the class associated with the given primitive type.
  • InetAddress.ofLiteral(String): creates an InetAddress from the textual representation of the IP address. This static method also exists for the Inet4Address and Inet6Address classes.
  • RandomGenerator.equiDoubles(double left, double right, boolean isLeftIncluded, boolean isRightIncluded).

All the new JDK 21 APIs can be found in The Java Version Almanac – New APIs in Java 22.

Internal changes, performance, and security

The G1 Garbage Collector has seen an improvement when a JNI (Java Native Interface) call defines a critical region. Previously, G1 was totally disabled, with the risk of blocking application threads requiring a GC, or even out of memory.

Thanks to JEP 423: Region Pinning for G1, G1GC is now able to pin only a single region in the event of a JNI critical section, avoiding blocking other application threads requiring a GC. More information in the JEP 423.

Parallel GC and Serial GC also have seen some optimisations in the card table scaning area (card table stores old-to-young references). Other changes on the Garbage Collector side can be found in this article by Thomas Schatzl: JDK 22 G1/Parallel/Serial GC changes.

On the security side, new root certificates have been added, and you can now use java -XshowSettings:security to see the security-related JVM configuration information. You can refer to Sean Mullan’s article for an exhaustive list of security changes included in this release: JDK 22 Security Enhancements

JFR Events

Here are the new Java Flight Recorder (JFR) events of the JVM :

  • CompilerQueueUtilization: no description.
  • NativeLibraryLoad: information on a dynamic library or other native image loading operation.
  • NativeLibraryUnload: information on a dynamic library or other native image unload operation.
  • DeprecatedInvocation: unique invocation of a method annotated with @Deprecated.

You can find all the JFR events supported in this version of Java on the page JFR Events.

Conclusion

One might have thought that Java 22 would be a stabilizing release after version 21, which is LTS, but no, there’s a major addition in the form of Stream Gatherer and a number of JEPs designed to simplify the language and make it easier to use. Also noteworthy is that the Foreign Function & Memory API goes out of preview, which will enable simplified use of native functions in Java, with a high-performance API that’s easier to use than JNI.

To find all the changes in Java 22, refer to therelease notes.

2 thoughts on “Java 22: what’s new?

  1. Merci pour ce chouette tour d’horizon.
    Petits typos à noter dans la section “ListFormat”: celons -> selon.
    Beau travail en tout cas, merci encore.

    Cordialement.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.