Java 22 APIs & Features

Java 22 introduces major improvements for native interoperability, code clarity, and stream processing. The Foreign Function & Memory API is now standard, enabling safe and efficient access to native code and memory. Unnamed variables and patterns simplify code, while Stream Gatherers provide powerful new stream operations. This release focuses on developer productivity, performance, and modern Java programming techniques.

Released: March 2024
Standard Release

Java 22 Overview

Release Information

Release Date:

March 2024

Support Type:
Standard Release

Quick Summary

Java 22 introduces major improvements for native interoperability, code clarity, and stream processing. The Foreign Function & Memory API is now standard, enabling safe and efficient access to native code and memory. Unnamed variables and patterns simplify code, while Stream Gatherers provide powerful new stream operations. This release focuses on developer productivity, performance, and modern Java programming techniques.

Top Features

  • Foreign Function & Memory API (Standard) - Safe and efficient native interoperability
  • Unnamed Variables & Patterns - Cleaner, more readable code
  • String Templates (Second Preview) - Modern string interpolation
  • Stream Gatherers (Preview) - Advanced stream processing capabilities
  • Structured Concurrency (Second Preview) - Simplified concurrent programming
  • Scoped Values (Second Preview) - Efficient data sharing in concurrency

API Examples

Unnamed Variables & Patterns

Java 22 introduces unnamed variables and patterns, allowing developers to use underscores (_) for unused variables in lambdas, catch blocks, switch cases, and record patterns. This feature improves code readability and reduces boilerplate, making it easier to focus on relevant data and logic.

// Unnamed variables in lambda expressions
map.forEach((key, _) -> System.out.println("Key: " + key));

// Unnamed variables in catch blocks
try {
    riskyOperation();
} catch (IOException _) {
    // Don't need the exception details
    System.out.println("IO operation failed");
}

// Unnamed patterns in switch
switch (shape) {
    case Circle(_, double radius) -> System.out.println("Circle with radius: " + radius);
    case Rectangle(_, _, double width, double height) -> 
        System.out.println("Rectangle: " + width + "x" + height);
    default -> System.out.println("Unknown shape");
}

// Multiple unnamed variables
for (int i = 0, _ = expensive(); i < 10; i++) {
    // expensive() is called only once
    System.out.println("Iteration: " + i);
}

// Unnamed variables in record patterns
record Point3D(double x, double y, double z) {}

public void processPoint(Point3D point) {
    switch (point) {
        case Point3D(double x, _, _) when x > 0 -> 
            System.out.println("Point with positive x: " + x);
        case Point3D(_, double y, _) when y > 0 -> 
            System.out.println("Point with positive y: " + y);
        default -> System.out.println("Point at origin or negative coordinates");
    }
}

Stream Gatherers (Preview)

Java 22 introduces Stream Gatherers as a preview feature, enabling advanced intermediate operations for stream processing. Gatherers allow for windowing, scanning, and custom element grouping, making it easier to implement complex data processing pipelines in a functional style.

import java.util.stream.Gatherers;
import java.util.stream.Stream;

// Built-in gatherers
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

// Windowing gatherer
List<List<Integer>> windows = numbers.stream()
    .gather(Gatherers.windowFixed(3))
    .toList();
// Result: [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]

// Scanning gatherer (running total)
List<Integer> runningSum = numbers.stream()
    .gather(Gatherers.scan(0, Integer::sum))
    .toList();
// Result: [0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55]

// Custom gatherer for duplicating elements
var duplicator = Gatherer.ofSequential(
    StringBuilder::new,
    (state, element, downstream) -> {
        downstream.push(element);
        downstream.push(element);
        return true;
    }
);

List<Integer> duplicated = Stream.of(1, 2, 3)
    .gather(duplicator)
    .toList();
// Result: [1, 1, 2, 2, 3, 3]

// Combining gatherers
List<String> result = Stream.of("apple", "banana", "cherry", "date")
    .gather(Gatherers.windowFixed(2))
    .map(window -> String.join(" & ", window))
    .toList();
// Result: ["apple & banana", "cherry & date"]

Foreign Function & Memory API (Standard)

Java 22 standardizes the Foreign Function & Memory API, enabling safe, efficient, and high-performance access to native code and memory from Java. This feature replaces JNI for most use cases, making it easier to call C libraries, manage native memory, and build high-performance, cross-platform applications.

import java.foreign.MemorySegment;
import java.foreign.MemorySession;
import java.foreign.Linker;
import java.foreign.SymbolLookup;

// Allocate native memory
try (MemorySession session = MemorySession.openConfined()) {
    MemorySegment segment = session.allocate(100);
    // Use the segment for native operations
}

// Call a native function (e.g., strlen from libc)
Linker linker = Linker.nativeLinker();
SymbolLookup libc = SymbolLookup.loaderLookup();
var strlen = linker.downcallHandle(
    libc.lookup("strlen").get(),
    FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS)
);
long len = (long) strlen.invoke(MemorySegment.ofArray("hello".getBytes()));