Java 23

Better Language, Better APIs, Better Runtime

Developer Advocate

Java Team at Oracle

Let’s get started!

Lots to talk about!

Final features in Java 23:

  • Markdown in JavaDoc

  • generational ZGC by default

New preview features:

  • primitive patterns

  • module imports

Lots to talk about!

Final features in Java 22:

  • unnamed patterns

  • FFM API

  • multi-source-file programs

Ongoing preview features:

  • string templates

  • statements before super

  • stream gatherers

  • class-file API

Java 23

Java 23 Features
Markdown in JavaDoc
GenZGC by Default
New Preview Features
Java 22 Features
Ongoing Preview Features

Writing JavaDoc

Writing simple JavaDoc is great!

Writing more complex documentation…​

  • where does <p> go?

  • do we need </p>?

  • code snippets/blocks are cumbersome

  • lists are verbose

  • tables are terrible

  • …​

I blame HTML!

Markdown

Markdown is more pleasant to write:

  • no <p> nor </p>

  • code snippts/blocks are simple

  • lists are simple

  • tables are terrible (but less so)

  • embedding HTML is straightforward

Markdown is widely used and known.

Markdown in JavaDoc

Java now allows Markdown JavaDoc:

  • each line starts with ///

  • most Commonmark 0.3 syntax works

  • links to program elements use extended
    reference link syntax: [text][element]

  • JavaDoc tags work as usual

[Introduced in Java 23 — JEP 467]

Why /// ?

Wouldn’t this be nice:

/**md
 *
 * Markdown here...
 *
 */

Why /// ?

No - reason #1:

/**md
 *
 * Here's a list:
 *
    * item #1
    * item #1
 *
 */

(The leading * in JavaDoc is optional.)

Why /// ?

No - reason #2:

/**md
 *
 * ```java
 * /* a Java inline comment */
 * ```
 *
 */

(/** can’t contain */.)

Why /// ?

///:

  • no such issues

  • doesn’t require new Java syntax
    (// already "escapes" parsing)

More

Java 23

Java 23 Features
Markdown in JavaDoc
GenZGC by Default
New Preview Features
Java 22 Features
Ongoing Preview Features

Generational ZGC

Compared to other GCs, ZGC:

  • optimizes for ultra-low pause times

  • can have higher memory footprint or higher CPU usage

In JDK 21, ZGC became generational.

Generational Hypothesis

  • most objects die young

  • those who don’t, grow (very) old

GCs can make use of this by tracking
young and old generations.

ZGC didn’t do this, but can do it now.

Reports

Netflix published a blog post on their adoption.

Out of context graphs (vs. G1):

netflix genzgc memory

Reports

netflix genzgc gc pause

Default

In JDK 23, generational mode is the default for ZGC:

-XX:+UseZGC

(Default GC is still G1.)

More

Java 23

Java 23 Features
New Preview Features
Primitive Patterns
Module Imports
Java 22 Features
Ongoing Preview Features

Patterns so far…​

In instanceof and switch, patterns can:

  • match against reference types

  • deconstruct records

  • nest patterns

  • ignore parts of a pattern

In switch:

  • refine the selection with guarded patterns

Patterns so far…​

That (plus sealed types) are
the pattern matching basics.

This will be:

  • built up with more features

  • built out to re-balance the language

Case in Point

The x instanceof Y operation:

  • meant: "is x of type Y?"

  • now means: "does x match the pattern Y?"

For primitives:

  • old semantics made no sense
    โ‡ no x instanceof $primitive

  • new semantics can make sense

Bound Checks

Example: int number = 0;

Can number be an instance of byte?

No, it’s an รฌnt.

But can its value be a byte?

Yes!

Bound Checks

int x = 0;
if (x instanceof byte b)
	System.out.println(b + " in [-128, 127]");
else
	System.out.println(x + " not in [-128, 127]");

More Checks

What’s special about 16_777_216?

Smallest positive int that float can’t represent.

Precision Checks

int x = 16_777_216;
if (x instanceof float f)
	// use `f`

Tri-state Boolean

Boolean bool = // ...
var emoji = switch (bool) {
	case null -> ""
	case true -> "โœ…";
	case false -> "โŒ";
}

(Bugged in 23; fixed in 23.0.1 and 24-EA.)

More

Java 23

Java 23 Features
New Preview Features
Primitive Patterns
Module Imports
Java 22 Features
Ongoing Preview Features

Imports

Which one do you prefer?

// option A
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

// option B
import java.util.*;

Star Imports

Upsides:

  • more succinct

  • easier to manage manually

Downsides:

  • less clear

  • chance of conflicts

  • arbitrary slice of API

Module Imports

import module $mod;
  • imports public API of $mod

  • your code does not need to be in a module

[Preview in Java 23 — JEP 476]

Module Import Details

import module $mod;

Imports all public top-level types in:

  • packages exported by $mod

  • packages exported by $mod to your module
    (qualified exports)

  • packages exported by modules that $mod
    requires transitively (implied readability)

Module Imports

Upsides:

  • much more succinct

  • trivial to manage manually

Downsides:

  • less detailed

  • conflicts are more likely

Conflicts

import module java.base;
import module java.desktop;
import module java.sql;

public class Example {

	public void example() {
		// error: reference to Date is ambiguous
		var outdated = new Date(1997, 1, 18);
		// error: reference to List is ambiguous
		var letters = List.of("I", "J", "N");
	}

}

Resolving Conflicts

import module java.base;
import module java.desktop;
import module java.sql;

import java.util.Date;
import java.util.List;

public class Example {

	public void example() {
		var outdated = new Date(1997, 1, 18);
		var letters = List.of("I", "J", "N");
	}

}

Use Cases

Consider using module imports, when:

  • you’re already using star imports

  • you’re writing scripts, experiments, demos, etc.

Default Import

Implicitly declared classes, implicitly import java.base:

// complete Main.java - no explicit imports!
void main() {
	List<?> dates = Stream
		.of(1, 2, 23, 29)
		.map(BigDecimal::new)
		.map(day -> LocalDate.of(
			2024,
			RandomGenerator.getDefault()
				.nextInt(11) + 1,
			day.intValue()))
		.toList();

	System.out.println(dates);
}

More

Deprecations

Cleaning house

Already removed:

Cleaning house

Deprecations (for removal):

  • memory access via Unsafe ใ‰“ (JEP 471)

  • allowed-by-default dynamic agents ใ‰‘ (JEP 451)

  • Windows 32-bit x86 port ใ‰‘ (JEPs 449, 479)

  • finalization โ‘ฑ (JEP 421)

  • security manager โ‘ฐ (JEP 411)

  • applet API โ‘ฐ (JEP 398)

  • primitive wrapper constructors โ‘ฏ (JEP 390)

  • biased locking โ‘ฎ (JEP 374)

Unsafe

Methods for memory access have been superseeded
(VarHandles via JEP 193; FFM via JEP 454).

Will be phased out:

  • deprecation and compile-time warning ใ‰“

  • run-time warning ~ใ‰•

  • exception on invocation ~ใ‰–

  • remove methods

Unsafe

You’re probably not using it directly,
but your dependencies may.

Find them with --sun-misc-unsafe-memory-access:

  • warn: to get run-time warnings today

  • debug: same with more info

  • deny: throws exception

Report and help fix!

Agents

What it is:

  • a component that transforms byte code

  • uses java.lang.instrument or JVM TI

  • launches with JVM or attaches later ("dynamic")

Dynamic agents

What you need to know:

  • all mechanisms for agents remain intact

  • nothing changed yet

  • in the future, dynamic attach will be
    disabled by default

  • enable with -XX:+EnableDynamicAgentLoading

Dynamic agents

What you need to do:

  • run your app with -XX:-EnableDynamicAgentLoading

  • observe closely

  • investigate necessity of dynamic agents

Finalization

What it is:

  • finalize() methods

  • a JLS/GC machinery for them

Finalization

What you need to know:

  • you can disable with --finalization=disabled

  • in a future release, disabled will be the default

  • in a later release, finalization will be removed

Finalization

What you need to do:

  • search for finalize() in your code and
    replace with try-with-resources or Cleaner API

  • search for finalize() in your dependencies and
    help remove them

  • run your app with --finalization=disabled and
    closely monitor resource behavior (e.g. file handles)

Security Manager

What it is:

  • a system of checks and permissions

  • intended to safeguard security-relevant
    code sections

  • embodied by SecurityManager

Security Manager

What you need to know:

  • barely used but maintenance-intensive

  • already disallowed by default

  • enable with java.security.manager=allow

  • in a future release, it will be removed

Security Manager

What you need to do:

  • observe your app with default settings
    (โ‡ security manager disallowed)

  • if used, move away from security manager

Primitive constructors

What it is:

  • new Integer(42)

  • new Double(42)

  • etc.

Primitive constructors

What you need to know:

  • Valhalla wants to turn them into value types

  • those have no identity

  • identity-based operations need to be removed

Primitive constructors

What you need to do:

  • Integer.valueOf(42)

  • Double.valueOf(42)

  • etc.

More

Java 23

Java 23 Features
New Preview Features
Java 22 Features
Unnamed Patterns
FFM API
Launch Multi-File Programs
Ongoing Preview Features

A simple app

Features:

  • scrapes GitHub projects

  • creates Page instances:

    • GitHubIssuePage

    • GitHubPrPage

    • ExternalPage

    • ErrorPage

  • further processes pages

A simple app

Features:

  • display as interactive graph

  • compute graph properties

  • categorize pages by topic

  • analyze mood of interactions

  • process payment for analyses

  • etc.

A simple architecture?

How to implement features?

  • methods on Page ๐Ÿ˜ง

  • visitor pattern ๐Ÿ˜ซ

  • pattern matching ๐Ÿฅณ

Pattern Matching

Ingredients:

  • switch expressions โ‘ญ (JEP 361)

  • type pattern matching โ‘ฏ (JEP 394)

  • sealed types โ‘ฐ (JEP 409)

  • patterns in switch ใ‰‘ (JEP 441)

  • unnamed patterns ใ‰’ (JEP 456)

Pattern Matching

Approach:

  • make Page sealed

  • implement features as methods outside of Page

  • accept Page parameters and switch over it

  • avoid default branch for maintainability

Sealed Page

Sealed types limit inheritance,
by only allowing specific subtypes.

public sealed interface Page
	permits GitHubIssuePage, GitHubPrPage,
			ExternalPage, ErrorPage {
	// ...
}

Switch over Page

public void categorize(Page page) {
	switch (page) {
		case GitHubIssuePage is -> categorizeIssue(is);
		case GitHubPrPage pr -> categorizePr(pr);
		case ExternalPage ext -> categorizeExternal(ext);
		case ErrorPage err -> categorizeError(err);
	}
}

Maintainability

Unlike an if-else-if-chain,
a pattern switch needs to be exhaustive.

Fulfilled by:

  • switching over a sealed types

  • a case per subtype

  • avoiding the default branch

โ‡ Adding a new subtype causes compile error!

New Page Type

If GitHubCommitPage is added:

public void categorize(Page page) {
	// error:
	//     "the switch statement does not cover
	//      all possible input values"
	switch (page) {
		case GitHubIssuePage is -> categorizeIssue(is);
		case GitHubPrPage pr -> categorizePr(pr);
		case ExternalPage ext -> categorizeExternal(ext);
		case ErrorPage err -> categorizeError(err);
	}
}

Avoiding Default

Sometimes you have "defaulty" behavior:

public void categorize(Page page) {
	switch (page) {
		case GitHubIssuePage is -> categorizeIssue(is);
		case GitHubPrPage pr -> categorizePr(pr);
		default -> { }
	}
}

But we need to avoid default!

Avoiding Default in Java 21

Write explicit branches:

public void categorize(Page page) {
	switch (page) {
		case GitHubIssuePage is -> categorizeIssue(is);
		case GitHubPrPage pr -> categorizePr(pr);
		// duplication ๐Ÿ˜ข
		case ErrorPage err -> { };
		case ExternalPage ext -> { };
	};
}

This is the state-of-the-art in Java 21
(without preview features).

Avoiding Default in Java 22

Use _ to combine "default branches":

public void categorize(Page page) {
	switch (page) {
		case GitHubIssuePage is -> categorizeIssue(is);
		case GitHubPrPage pr -> categorizePr(pr);
		case ErrorPage _, ExternalPage _ -> { };
	};
}

โ‡ Default behavior without default branch. ๐Ÿฅณ

More

Java 23

Java 23 Features
New Preview Features
Java 22 Features
Unnamed Patterns
FFM API
Launch Multi-File Programs
Ongoing Preview Features

Foreign memory

Storing data off-heap is tough:

  • ByteBuffer is limited (2GB) and inefficient

  • Unsafe is…​ unsafe and not supported

Foreign-memory API

Panama introduces safe and performant API:

  • control (de)allocation:
    Arena, MemorySegment, SegmentAllocator

  • to access/manipulate: MemoryLayout, VarHandle

Foreign-memory API

// create `Arena` to manage off-heap memory lifetime
try (Arena offHeap = Arena.ofConfined()) {
	// [allocate off-heap memory to store pointers]
	// [do something with off-heap data]
	// [copy data back to heap]
} // off-heap memory is deallocated here

Foreign-memory API

Allocate off-heap memory to store pointers:

String[] javaStrings = { "mouse", "cat", "dog" };
// Arena offHeap = ...

MemorySegment pointers = offHeap.allocateArray(
	ValueLayout.ADDRESS, javaStrings.length);
for (int i = 0; i < javaStrings.length; i++) {
	// allocate off-heap & store a pointer
	MemorySegment cString = offHeap
		.allocateUtf8String(javaStrings[i]);
	pointers
		.setAtIndex(ValueLayout.ADDRESS, i, cString);
}

Foreign-memory API

Copy data back to heap:

// String[] javaStrings = ...
// MemorySegment pointers =

for (int i = 0; i < javaStrings.length; i++) {
	MemorySegment cString = pointers
		.getAtIndex(ValueLayout.ADDRESS, i);
	javaStrings[i] = cString.getUtf8String(0);
}

Foreign functions

JNI isn’t ideal:

  • involves several tedious artifacts (header file, impl, …​)

  • can only interoperate with languages that align
    with OS/architecture the JVM was built for

  • doesn’t reconcile Java/C type systems

Foreign-function API

Panama introduces streamlined tooling/API
based on method handles:

  • jextract: generates method handles from header file

  • classes to call foreign functions
    Linker, FunctionDescriptor, SymbolLookup

Foreign-function API

// find foreign function on the C library path
Linker linker = Linker.nativeLinker();
SymbolLookup stdlib = linker.defaultLookup();
MethodHandle radixSort = linker
	.downcallHandle(stdlib.find("radixsort"), ...);

String[] javaStrings = { "mouse", "cat", "dog" };
try (Arena offHeap = Arena.ofConfined()) {
	// [move Java strings off heap]
	// invoke foreign function
	radixSort.invoke(
		pointers, javaStrings.length,
		MemorySegment.NULL, '\0');
	// [copy data back to heap]
}

Finally final!

Java 22 finalizes the FFM API, but there’s more to do:

  • user-friendly and performant mapping from
    native memory to Java records/interfaces

  • improving jextract and surrounding tooling

And more.

More

Java 23

Java 23 Features
New Preview Features
Java 22 Features
Unnamed Patterns
FFM API
Launch Multi-File Programs
Ongoing Preview Features

A Mature Ecosystem

Java is very mature:

  • refined programming model

  • detailed toolchain

  • rich ecosystem

But this can make it hard to learn for new (Java) developers.

Approachable Java

Java needs to be approachable:

  • for kids

  • for students

  • for the frontend dev

  • for ML/AI folks

  • etc.

Java needs an on-ramp for new (Java) developers!

On-Ramp to Java

On-ramp:

  • simplified main method and class

  • single-source-file execution

  • multi-source-file execution

Simpler Code

Remove requirement of:

  • String[] args parameter

  • main being static

  • main being public

  • the class itself

// smallest viable Main.java
void main() {
	// ...
}

[Preview in Java 23 — JEP 477]

Simpler Code

Implicitly declared classes, implicitly import:

  • java.io.IO 's methods print, println, and readln

  • public top-level classes in packages exported by java.base

// complete Main.java
void main() {
	var letters = List.of("A", "B", "C");
	println(letters);
}

Single-File Execution

It’s easy to execute that file:

java Main.java

[Introduced in Java 11 — JEP 330]

Multi-File Execution

The program can expand:

MyFirstJava
 โ”œโ”€ Main.java
 โ”œโ”€ Helper.java
 โ””โ”€ Lib
     โ””โ”€ library.jar

Run with:

java -cp 'Lib/*' Main.java

[Introduced in Java 22 — JEP 458]

Progression

Natural progression:

  • start with main()

  • need arguments? โ‡ add String[] args

  • need to organize code? โ‡ add methods

  • need shared state? โ‡ add fields

  • need more functionality? โ‡ explore JDK APIs

  • even more? โ‡ explore simple libraries

  • need more structure? โ‡ split into multiple files

  • even more โ‡ use visibility & packages

Doesn’t even have to be that order!

More

Simplified main:

Single-source-file execution:

Multi-source-file execution:

Java 23

Java 23 Features
New Preview Features
Java 22 Features
Ongoing Preview Features
String Templates
Statements Before Super
Stream Gatherers
Class-File API

String composition

Composing strings in Java is cumbersome:

String property = "last_name";
String value = "Doe";

// concatenation
String query =
	"SELECT * FROM Person p WHERE p."
		+ property + " = '" + value + "'";
// formatting
String query =
	"SELECT * FROM Person p WHERE p.%s = '%s'"
		.formatted(property, value);

Comes with free SQL injection! ๐Ÿ˜ณ

String interpolation

Why not?

// (fictional syntax!)
String query =
	"SELECT * FROM Person p "
		+ "WHERE p.\{property} = '\{value}'";

Also comes with free SQL injection! ๐Ÿ˜ณ

String interpolation

SQL injections aren’t the only concern.

These also need validation and sanitization:

  • HTML/XML

  • JSON

  • YAML

  • …​

All follow format-specific rules.

String templates

[Preview in Java 21 and 22 — JEP 430, JEP 459]

Statement query = SQL."""
	SELECT * FROM Person p
	WHERE p.\{property} = '\{value}'
	""";

Special SQL."…​" syntax was too much.

Quo Vadis?

  • string templates were removed from JDK 23
    (not even a preview)

  • the feature needs a redesign

  • timing is unknown

๐Ÿ˜ž

More

Java 23

Java 23 Features
Java 22 Features
New Preview Features
Ongoing Preview Features
String Templates
Statements Before Super
Stream Gatherers
Class-File API

Constructor Chaining

With multiple constructors, it’s good practice
to have one constructor that:

  • checks all arguments

  • assigns all fields

Other constructors just forward (if possible).

Constructor Chaining

class Name {

	private final String first;
	private final String last;

	Name(String first, String last) {
		// [... checks for null, etc. ...]
		this.first = first;
		this.last = last;
	}

	Name(String last) {
		this("", last);
	}

}

Inheritance

With superclasses, chaining is enforced:

class ThreePartName extends Name {

	private final String middle;

	ThreePartName(
			String first, String middle, String last) {
		// doesn't compile without this call
		super(first, last);
		this.middle = middle;
	}

}

Limitations

But:

Java allows no statements before
super(…​) / this(…​)!

Why?

No statements before super(…​) / this(…​):

  • superclass should be initialized
    before subclass runs any code
    โ‡ no code before super(…​)

  • code before this(…​) would
    run before super(…​)
    โ‡ no code before this(…​)

Limitations

This is inconvenient when you want to:

  • check arguments

  • prepare arguments

  • split/share arguments

Splitting Arguments

class Name {

	// fields and constructor as before

	Name(String full) {
		// does the same work twice
		this(
			full.split(" ")[0],
			full.split(" ")[1]);
	}

}

Splitting Arguments

class Name {

	// fields and constructor as before

	// avoids two splits but "costs"
	// duplicated argument validation
	Name(String full) {
		String[] names = full.split(" ");
		// [... checks for null, etc. ...]
		this.first = names[0];
		this.last = names[1];
	}

}

Splitting Arguments

class Name {

	// fields and constructor as before

	// avoids two splits but "costs"
	// an additional constructor
	Name(String full) {
		this(full.split(" "));
	}

	private Name(String[] names) {
		this(names[0], names[1]);
	}

}

Splitting Arguments

class Name {

	// fields and constructor as before

	// avoids two splits but "costs"
	// an additional construction protocol
	static Name fromFullName(String full) {
		String[] names = full.split(" ");
		return new Name(names[0], names[1]);
	}

}

Limitations - Record Edition

To enforce a uniform construction protocol:

Records require all custom constructors
to (eventually) call the canonical constructor.

Limitations - Record Edition

record Name(String first, String last) {

	// nope
	Name(String full) {
		String[] names = full.split(" ");
		// [... checks for null, etc. ...]
		this.first = names[0];
		this.last = names[1];
	}

}

Splitting Arguments

What we want to write:

record Name {

	Name(String full) {
		String[] names = full.split(" ");
		this(names[0], names[1]);
	}

}

(Analogous for classes.)

Statements Before …​

Java 23 previews statements
before super(…​) and this(…​).

Great to…​

Check Arguments

class ThreePartName extends Name {

	private final String middle;

	ThreePartName(
			String first, String middle, String last) {
		// can't have a middle name without a first name
		requireNonNullNonEmpty(first);
		super(first, last);
		this.middle = middle;
	}

}

Prepare Arguments

class ThreePartName extends Name {

	private final String middle;

	ThreePartName(
			String first, String middle, String last) {
		// shorten first if middle is given
		var short1st = middle.length() == 1
				? first.substring(0, 1)
				: first;
		super(first, last);
		this.middle = middle;
	}

}

Split Arguments

class ThreePartName extends Name {

	private final String middle;

	ThreePartName(String full) {
		// split "first middle last" on space (once ๐Ÿ™Œ๐Ÿพ)
		var names = full.split(" ");
		super(names[0], names[2]);
		this.middle = names[1];
	}

}

Surprising Cameo

  • Project Valhalla ponders non-nullable types

  • has to work with super()

  • subclass sets non-null fields before super()

More

Java 23

Java 23 Features
Java 22 Features
New Preview Features
Ongoing Preview Features
String Templates
Statements Before Super
Stream Gatherers
Class-File API

Missing Stream Ops

Streams are great, but some
intermediate operations are missing:

  • sliding windows

  • fixed groups

  • take-while-including

  • scanning

  • increasing sequences

  • etc.

Missing Terminal Ops

Streams also don’t have all possible terminal operations.

Instead:

  • generalization for terminal ops โ‡ collectors

  • a few implementations, e.g. Collectors.toSet()

  • extension point for them: Stream::collect

Let’s do the same for intermediate ops!

Introducing Gatherers

The gatherers API consists of:

  • generalization for intermediate ops โ‡ gatherers

  • a few implementations, e.g. Gatherers.scan(โ€ฆ)

  • extension point for them: Stream::gather

Stream.of("A", "C", "F", "B", "S")
	.gather(scan(...))
	.forEach(System.out::println);

Gatherer Building Blocks

One required building block:

Integrator
  • accepts (state, element, downstream)

  • has the task to combine state and element

    • to update the state

    • to emit 0+ result(s) to downstream

Integrator Example

Behaves transparently:

static <T> Gatherer<T, ?, T> transparent() {
	Integrator<Void, T, T> integrator = (_, el, ds)
		-> ds.push(el);
	return Gatherer.of(integrator);
}

Integrator Example

Reimplements Stream::map:

static <T, R> Gatherer<T, ?, R> map(Function<T, R> f) {
	Integrator<Void, T, R> integrator = (_, el, ds) -> {
		R newEl = f.apply(el);
		return ds.push(newEl);
	};
	return Gatherer.of(integrator);
}

Gatherer Building Blocks

Three optional building blocks:

Initializer:

creates instance(s) of state

Finisher:
  • accepts (state, downstream)

  • emits 0+ element(s) to downstream

Combiner:

combines to states into one

Fixed-Sized Groups

Create groups of fixed size:

  • stream input: "A", "C", "F", "B", "S"

  • output of groups(2): ["A", "C"], ["F", "B"], ["S"]

We need:

  • an initializer to create empty group list

  • an integrator that emits when group is full

  • a finisher to emit incomplete group

Fixed-Sized Group Initializer

Supplier<List<T>> initializer = ArrayList::new;

Fixed-Sized Group Integrator

Integrator<List<T>, T, List<T>> integrator =
	(list, el, ds) -> {
		list.add(el);

		if (list.size() < size)
			return true;
		else {
			var group = List.copyOf(list);
			list.clear();
			return ds.push(group);
		}
	};

Fixed-Sized Group Finisher

BiConsumer<List<T>, Downstream<List<T>>> finisher =
	(list, ds) -> {
		var group = List.copyOf(list);
		ds.push(group);
	};

Fixed-Sized Group Gatherer

static <T> Gatherer<T, ?, List<T>> groups(int size) {
	Supplier<...> initializer = // ...
	Integrator<...> integrator = // ...
	BiConsumer<...> finisher = // ...

	return Gatherer.ofSequential(
		initializer, integrator, finisher);
}

Fixed-Sized Group Gatherer

Using our gatherer:

Stream.of("A", "C", "F", "B", "S")
	.gather(groups(2))
	.forEach(System.out::println);

// [A, C]
// [F, B]
// [S]

More

Java 23

Java 23 Features
Java 22 Features
New Preview Features
Ongoing Preview Features
String Templates
Statements Before Super
Stream Gatherers
Class-File API

Bytecode Basics

Bytecode is instruction set for JVM:

  • creating objects and arrays

  • copying variable values or references
    between stack and registers

  • invoking methods

  • computing arithmetic operations

  • etc.

Bytecode Basics

Basic lifecycle:

  • generated by javac

  • stored in .class files

  • loaded, parsed, verified by class loader

  • executed by JVM

Bytecode Beyond Basics

In real life, much more happens:

  • generated by frameworks at build time

  • turned into machine code by JIT compiler
    or Graal native image

  • prefetched by class-data sharing

  • analyzed by jdeps, SpotBugs, etc.

  • manipulated by agents and libraries

Bytecode Tools

Tooling:

  • libraries don’t manipulate bytecode themselves

  • they use a few tools

Big player is ASM
(direct or, e.g., via ByteBuddy or CGLIB).

Migration Pains

Updates:

  • bytecode has a level (e.g. 65 for Java 21)

  • tools can’t work with a higher level
    than they were built for

This can block updates!

E.g. when compiling your code with Java 25 (level 69)…​

Migration Pains

This is the reason for:

Before updating the JDK,
update all dependencies.

We want to move past that!

Class-File API

An API in Java that allows
analyzing and manipulating bytecode:

  • stable API in JDK

  • always up-to-date

When JDK is updated:

  • it may read new bytecodes

  • but that’s ok for most use cases

More

Java 23

In a few slides…​

Java 23 Introduces

Java 23 Previews

Java 23 Continues

Java 23

Not explosive like Java 23,
but no slouch either.

Continues Java’s evolution.

Get it at jdk.java.net/23.

So long…​

37% off with
code fccparlog

bit.ly/the-jms

More

Slides at slides.nipafx.dev
โ‡œ Get my book!

Follow Nicolai

nipafx.dev
/nipafx

Follow Java

inside.java // dev.java
/java    //    /openjdk

Image Credits