Java 22 & 23: Unnamed Variables and Foreign Memory
Java 22 finalised the Foreign Function and Memory API and introduced unnamed variables and patterns. Java 23 continued refining previews including primitive types in patterns and module import declarations. Together they represent the platform's ongoing maturation.
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 usetry { 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 discardtry { riskyOperation();} catch (IOException _) { log.warn("Operation failed, retrying...");}// In enhanced for loopsfor (var _ : list) { count++;}
// In try-with-resourcestry (var _ = ScopedContext.acquire()) { // use the context, but don't need the reference doWork();}
// Ignoring return valuesvar _ = queue.offer(item); // explicitly ignoring the boolean returnUnnamed 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 zif (obj instanceof Point(int x, _, _)) { System.out.println("X coordinate: " + x);}
// In switchString 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:
- Call native functions in C (and other native) libraries without writing JNI code
- 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 functiontry (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 closesWorking 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 hereArena Types
| Arena | Lifetime | Thread Safety |
|---|---|---|
Arena.ofAuto() | GC-managed | Thread-safe |
Arena.ofConfined() | Manual (try-with-resources) | Single thread only |
Arena.ofShared() | Manual (try-with-resources) | Thread-safe |
Arena.global() | Never freed | Thread-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:
# Run a program consisting of multiple source filesjava Main.javaIf 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 instanceofObject obj = 42;
if (obj instanceof int i) { System.out.println("Integer value: " + i);}
// Primitive types in switchstatic 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 importsimport 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 moduleimport module java.base;
// Now all types from java.base are available without individual importsList<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 stringpublic 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.
| Feature | Version | Key Benefit |
|---|---|---|
| Unnamed Variables & Patterns (Final) | 22 | Explicit discard with _; cleaner code |
| Foreign Function & Memory API (Final) | 22 | Native code without JNI; safe off-heap memory |
| Multi-File Source Programs | 22 | Run multi-file programs without compilation |
| Primitive Types in Patterns (Preview) | 23 | Match primitives in instanceof and switch |
| Module Import Declarations (Preview) | 23 | Import entire modules with one statement |
| Markdown in Javadoc | 23 | Write documentation in Markdown |