Java Evolution — Part 10

Java 22 (March 19, 2024) and Java 23 (September 17, 2024) were non-LTS releases that finalised several long-incubating APIs and introduced new language refinements. Java 22 is notable for finalising the Foreign Function & Memory API — a complete replacement for JNI.


Unnamed Variables and Patterns — Finalised (Java 22, JEP 456)

Unnamed variables and patterns allow you to use _ (underscore) as a placeholder for variables and pattern components you intentionally do not use. This makes code clearer by explicitly signalling that a value is being discarded.

Unnamed Variables

// Before Java 22 — you had to name variables you don't use
try {
riskyOperation();
} catch (IOException e) { // e is never used — but you had to name it
log.warn("Operation failed, retrying...");
}
// Java 22 — use _ to signal intentional discard
try {
riskyOperation();
} catch (IOException _) {
log.warn("Operation failed, retrying...");
}
// In enhanced for loops
for (var _ : list) {
count++;
}
// In try-with-resources
try (var _ = ScopedContext.acquire()) {
// use the context, but don't need the reference
doWork();
}
// Ignoring return values
var _ = queue.offer(item); // explicitly ignoring the boolean return

Unnamed Patterns

In pattern matching, _ can replace a type pattern you do not need to bind:

record Point(int x, int y, int z) {}
Object obj = new Point(1, 2, 3);
// Only care about x — ignore y and z
if (obj instanceof Point(int x, _, _)) {
System.out.println("X coordinate: " + x);
}
// In switch
String describe(Object o) {
return switch (o) {
case Point(int x, int y, _) when x == y -> "Diagonal point";
case Point(int x, _, _) -> "Point with x=" + x;
default -> "Not a point";
};
}

Foreign Function & Memory API — Finalised (Java 22, JEP 454)

The Foreign Function & Memory API, incubated since Java 14 and previewed since Java 19, was finalised in Java 22. It provides a safe, efficient, and pure-Java way to:

  1. Call native functions in C (and other native) libraries without writing JNI code
  2. Access off-heap memory with well-defined lifetime management

Calling a Native Function

import java.lang.foreign.*;
import java.lang.invoke.MethodHandle;
// Call the C standard library's strlen function
try (Arena arena = Arena.ofConfined()) {
// Look up the function in the C standard library
Linker linker = Linker.nativeLinker();
SymbolLookup stdlib = linker.defaultLookup();
MethodHandle strlen = linker.downcallHandle(
stdlib.find("strlen").orElseThrow(),
FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS)
);
// Allocate a C string in off-heap memory
MemorySegment cString = arena.allocateFrom("Hello, native world!");
// Call strlen
long length = (long) strlen.invoke(cString);
System.out.println("Length: " + length); // 20
}
// Memory is automatically freed when the arena closes

Working with Off-Heap Memory

// Allocate a large off-heap buffer (not subject to GC pressure)
try (Arena arena = Arena.ofConfined()) {
// Allocate 1 MB of off-heap memory
MemorySegment buffer = arena.allocate(1024 * 1024);
// Write structured data
MemoryLayout pointLayout = MemoryLayout.structLayout(
ValueLayout.JAVA_INT.withName("x"),
ValueLayout.JAVA_INT.withName("y")
);
VarHandle xHandle = pointLayout.varHandle(MemoryLayout.PathElement.groupElement("x"));
VarHandle yHandle = pointLayout.varHandle(MemoryLayout.PathElement.groupElement("y"));
MemorySegment point = arena.allocate(pointLayout);
xHandle.set(point, 0L, 10);
yHandle.set(point, 0L, 20);
System.out.println("x=" + xHandle.get(point, 0L)); // x=10
System.out.println("y=" + yHandle.get(point, 0L)); // y=20
} // off-heap memory freed here

Arena Types

ArenaLifetimeThread Safety
Arena.ofAuto()GC-managedThread-safe
Arena.ofConfined()Manual (try-with-resources)Single thread only
Arena.ofShared()Manual (try-with-resources)Thread-safe
Arena.global()Never freedThread-safe

Launch Multi-File Source Programs (Java 22, JEP 458)

Java 22 extended the ability to run source files directly (introduced in Java 11) to support multi-file programs. You can now run a Java program that spans multiple source files without compiling them first:

Terminal window
# Run a program consisting of multiple source files
java Main.java

If Main.java references classes defined in other .java files in the same directory, the JVM compiles and runs them all together. This makes Java significantly more practical for scripting and small utilities.


Primitive Types in Patterns (Preview — Java 23, JEP 455)

Java 23 began a preview of primitive type patterns in instanceof and switch. Previously, patterns only worked with reference types. This extension allows patterns to match and bind primitive types directly.

// Java 23 preview — primitive types in instanceof
Object obj = 42;
if (obj instanceof int i) {
System.out.println("Integer value: " + i);
}
// Primitive types in switch
static String describe(Object o) {
return switch (o) {
case int i when i < 0 -> "negative int: " + i;
case int i -> "non-negative int: " + i;
case long l -> "long: " + l;
case double d -> "double: " + d;
case String s -> "string: " + s;
default -> "other";
};
}
describe(42); // "non-negative int: 42"
describe(-5); // "negative int: -5"
describe(3.14); // "double: 3.14"

This feature was finalised in Java 25.


Module Import Declarations (Preview — Java 23, JEP 476)

Java 23 previewed a new import syntax that imports all exported packages of a module at once:

// Before — individual imports
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
// Java 23 preview — import the entire java.base module
import module java.base;
// Now all types from java.base are available without individual imports
List<String> names = Stream.of("Alice", "Bob")
.collect(Collectors.toList());

This is particularly useful in exploratory code, JShell sessions, and small programs. This feature was finalised in Java 25.


Markdown in Javadoc (Java 23, JEP 467)

Java 23 introduced support for writing Javadoc comments in Markdown instead of HTML. A Markdown Javadoc comment is identified by /// instead of /**:

/// Returns the greeting for the given name.
///
/// ## Example
///
/// ```java
/// String greeting = greet("Alice");
/// assert greeting.equals("Hello, Alice!");
/// ```
///
/// @param name the name to greet
/// @return the greeting string
public String greet(String name) {
return "Hello, " + name + "!";
}

Summary

Java 22 and 23 continued the platform’s steady improvement, with the Foreign Function & Memory API finalisation being the most significant milestone.

FeatureVersionKey Benefit
Unnamed Variables & Patterns (Final)22Explicit discard with _; cleaner code
Foreign Function & Memory API (Final)22Native code without JNI; safe off-heap memory
Multi-File Source Programs22Run multi-file programs without compilation
Primitive Types in Patterns (Preview)23Match primitives in instanceof and switch
Module Import Declarations (Preview)23Import entire modules with one statement
Markdown in Javadoc23Write documentation in Markdown