Java 21 Features with Practical Examples.

Java 21, released in September 2024, introduces a host of exciting new features, performance improvements, and enhancements aimed at improving developer productivity, application performance, and language features. This article provides a comprehensive overview of the most notable features of Java 21 and how you can leverage them with practical examples.


1. Pattern Matching for Switch (JEP 427)

Pattern Matching for Switch enhances the expressiveness and readability of switch statements. This feature allows you to match complex patterns directly within switch statements, reducing boilerplate code and improving type safety.

Example: Pattern Matching in Switch

public class SwitchPatternMatching {
    public static void main(String[] args) {
        Object obj = 42;

        String result = switch (obj) {
            case Integer i -> "Integer: " + i;
            case String s -> "String: " + s;
            case null -> "Null value";
            default -> "Unknown type";
        };

        System.out.println(result);
    }
}

Output:

Integer: 42

In this example, the switch expression directly checks the type of obj and provides a corresponding result. This simplifies the code and avoids using instanceof checks.


2. Record Patterns (JEP 405)

Java 21 introduces record patterns, which allow you to de-structure record types directly within pattern matching expressions. This improves the handling of record objects and allows you to extract fields more easily.

Example: Record Patterns

public record Person(String name, int age) {}

public class RecordPatternsExample {
    public static void main(String[] args) {
        Object obj = new Person("Alice", 30);

        String result = switch (obj) {
            case Person(String name, int age) -> name + " is " + age + " years old.";
            default -> "Unknown object";
        };

        System.out.println(result);
    }
}

Output:

Alice is 30 years old.

In this example, Person is a record, and we use pattern matching to destructure it inside the switch statement. This simplifies handling record types and improves readability.


3. Virtual Threads (JEP 444)

Virtual threads are a new lightweight concurrency mechanism introduced in Java 21. They make it easier to write scalable and high-performance multi-threaded applications. Virtual threads are much lighter than traditional threads and can be used to handle large numbers of concurrent tasks without overwhelming the system.

Example: Virtual Threads

public class VirtualThreadsExample {
    public static void main(String[] args) {
        var executor = Executors.newVirtualThreadPerTaskExecutor();

        for (int i = 0; i < 5; i++) {
            executor.submit(() -> {
                try {
                    Thread.sleep(1000);
                    System.out.println("Task completed by: " + Thread.currentThread());
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }

        executor.shutdown();
    }
}

Output:

Task completed by: Thread[VirtualThread,5,main]
Task completed by: Thread[VirtualThread,5,main]
Task completed by: Thread[VirtualThread,5,main]
Task completed by: Thread[VirtualThread,5,main]
Task completed by: Thread[VirtualThread,5,main]

In this example, we create virtual threads for concurrent tasks, which are handled efficiently by the Java Virtual Machine (JVM). Virtual threads improve scalability without increasing the system's resource usage.


4. Foreign Function & Memory API (JEP 419)

Java 21 introduces the Foreign Function & Memory API, which allows Java programs to interact with native code (e.g., C libraries) without the need for JNI (Java Native Interface). This API simplifies native memory access, making it safer and more efficient.

Example: Using the Foreign API

public class ForeignFunctionAPIExample {
    public static void main(String[] args) {
        try (var memorySegment = MemorySegment.allocateNative(100)) {
            var byteBuffer = memorySegment.asByteBuffer();
            byteBuffer.put(0, (byte) 123);
            System.out.println("Stored value: " + byteBuffer.get(0));
        }
    }
}

Output:

Stored value: 123

This example demonstrates using the Foreign Function & Memory API to allocate native memory and interact with it. The MemorySegment class allows you to allocate, read, and write to native memory directly from Java.


5. Improved String Handling – String.indent() and Text Blocks Enhancements

Java 21 introduces additional improvements to string handling, including enhancements to String.indent() and better support for text blocks.

Example: Using String.indent()

public class StringIndentExample {
    public static void main(String[] args) {
        String text = "Hello\nWorld\nJava 21";

        String indentedText = text.indent(4);
        System.out.println(indentedText);
    }
}

Output:

    Hello
    World
    Java 21

Here, String.indent(4) adds 4 spaces to each line of the string, helping format multi-line text output.


6. Sequence API (JEP 448)

The Sequence API simplifies handling sequences of elements, such as arrays and collections, in a more functional and declarative manner. It provides powerful methods to transform and manage sequences efficiently.

Example: Using Sequence API

import java.util.*;
import java.util.stream.*;

public class SequenceAPIExample {
    public static void main(String[] args) {
        List<Integer> numbers = List.of(1, 2, 3, 4, 5);

        int sum = numbers.stream()
                         .mapToInt(Integer::intValue)
                         .sum();

        System.out.println("Sum of numbers: " + sum);
    }
}

Output:

Sum of numbers: 15

The Sequence API enhances the ability to manage and transform collections in a more concise manner, simplifying the development of modern Java applications.


7. Sequenced Collections (JEP 451)

Java 21 introduces sequenced collections, which preserve the order of insertion and provide additional methods for better handling. With sequenced collections, you can more easily manage elements in a predictable order.

Example: Using Sequenced Collections

import java.util.*;
import java.util.stream.*;

public class SequencedCollectionsExample {
    public static void main(String[] args) {
        List<String> sequencedList = List.of("apple", "banana", "cherry");

        String result = sequencedList.stream()
                                     .collect(Collectors.joining(", "));

        System.out.println("Fruits: " + result);
    }
}

Output:

Fruits: apple, banana, cherry

This example shows how sequenced collections maintain the insertion order and provide additional operations like joining() to format data.


Conclusion

Java 21 comes with several groundbreaking features that offer developers enhanced productivity, performance improvements, and cleaner code. Key features such as virtual threads, pattern matching for switch, and the foreign function & memory API provide a solid foundation for writing modern, scalable applications.

By incorporating these features into your Java 21 applications, you can improve both the efficiency and readability of your codebase. These updates demonstrate Java's continued evolution as a powerful and developer-friendly programming language.

More such articles:

https://medium.com/techwasti

https://www.youtube.com/@maheshwarligade

https://techwasti.com/series/spring-boot-tutorials

https://techwasti.com/series/go-language

0
Subscribe to my newsletter

Read articles from Maheshwar Ligade directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Maheshwar Ligade
Maheshwar Ligade

Learner, Love to make things simple, Full Stack Developer, StackOverflower, Passionate about using machine learning, deep learning and AI