Exploring Java 21 Features Through Practical Examples

  • 09 Feb 2024

Java 21 is scheduled for release on September 19, 2023, serving as the forthcoming long-term support (LTS) edition in Oracle's standard Java implementation. Java 21 will encompass a total of 15 noteworthy features:

  • String Templates (Preview) [JEP-430
  • Sequenced Collections [JEP-431
  • Generational ZGC [JEP-439]
  • Record Patterns [JEP-440
  • Pattern Matching for switch [JEP-441]
  • Foreign Function & Memory API (Third Preview) [JEP-442]
  • Unnamed Patterns and Variables (Preview) [JEP-443]
  • Virtual Threads [JEP-444
  • Unnamed Classes and Instance Main Methods (Preview) [JEP-445
  • Scoped Values (Preview) [JEP-446]
  • Vector API (Sixth Incubator) [JEP-448]
  • Deprecate the Windows 32-bit x86 Port for Removal [JEP-449]
  • Prepare to Disallow the Dynamic Loading of Agents [JEP-451]
  • Key Encapsulation Mechanism API [JEP-452]
  • Structured Concurrency (Preview) [JEP-453

Key Features of Java 21

Among other changes, the following are the main changes that we should be aware of as a Java developer.

  • Virtual Threads have been finalized.
  • The Record Patterns (Project Amber) have been finalized.
  • Pattern Matching for switch statements has been finalized.
  • A new collection, SequencedCollection, has been added that provides direct access to an ordered collection’s first and last elements.
  • String templates, unnamed classes and instance main() methods are available as preview features.

Virtual Threads (Project Loom)

Java 21's Virtual Threads: Boosting High-Concurrency Applications

Virtual threads, as managed by the JVM, are a game-changer for developing high-throughput concurrent applications. (Throughput refers to the number of units of information a system can process within a given time frame.) These advancements are encapsulated in JEP-425, JEP-436, and JEP-444, and in Java 21, virtual threads are primed for production deployment.

The introduction of virtual threads opens the door to executing countless virtual threads with just a handful of operating system threads. What's particularly advantageous is that no alterations are needed in existing Java code. The only requirement is to instruct our application framework to leverage virtual threads instead of platform threads.

To create a virtual thread, Java APIs provide options such as using the Thread class or Executors.

Sequenced Collections

The recently introduced interfaces as part of the sequenced collections initiative define collections with a clearly specified sequence order. This order encompasses a well-defined first element, second element, and continues in sequence until the final element. These interfaces offer a consistent API for accessing these elements in either the original sequence or in reverse order.

Furthermore, all widely adopted and frequently used collection classes have been updated to incorporate the java.util.SequencedCollection, java.util.SequencedSet, or java.util.SequencedMap interfaces that align with their respective collection types.

The newly introduced interfaces feature additional methods designed to facilitate sequential access to elements. For instance, the SequencedCollection includes the following methods

Note that any modifications to the original collection are visible in the reversed collection view also.

Let us understand with a Java program how we can use the new sequenced ArrayList:

ArrayList arrayList = new ArrayList<>(); addFirst.add(1); // [1] arrayList.addFirst(0); // [0, 1] arrayList.addLast(2); // [0, 1, 2] arrayList.getFirst(); // 0 arrayList.getLast(); // 2

To understand the benefits, see how these simple operations were too much verbose in Java 17.

//arrayList.get(arrayList.iterator().next());       // first element //arrayList.get(arrayList.size() - 1);                 // last element

Record Patterns

Records in Java are transparent and immutable carriers for data (similar to POJO). We create a record as follows:

record Point(int x, int y) {}

Previously, if need to access the components of a record, we have to destructure it as follows:

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

In Java 21, we can achieve a more concise representation using the record pattern syntax, such as Point(int x, int y). Record patterns streamline the process by removing the need to declare local variables for extracted components and automatically initializing these components through accessor methods when a value matches the pattern.

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

String Templates (Preview)

Using the string templates, we can create string templates containing embedded expressions (evaluated at runtime). The template strings can contain variables, methods or fields, computed at run time, to produce a formatted string as output.

Syntactically, a template expression resembles a string literal with a prefix.

let message = `Greetings ${ name }!`; //TypeScript

String message = STR."Greetings \{ name }!"; //Java

In the above template expression:

  1. STR is the template processor.
  2. There is a dot operator (.) between the processor and the expression.
  3. Template string with embedded expression. The expression is in the form of (\{name}).