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.
March 2024
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.
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");
}
}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"]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()));